
/*!
 * @since Last modified: 2025-3-29 21:6:38
 * @name AXUI front-end framework.
 * @version 3.0.35
 * @author AXUI development team <3217728223@qq.com>
 * @description The AXUI front-end framework is built on HTML5, CSS3, and JavaScript standards, with TypeScript used for type management.
 * @see {@link https://www.axui.cn|Official website}
 * @see {@link https://github.com/codady/axui/issues|github issues}
 * @see {@link https://gitee.com/codady/axui/issues|Gitee issues}
 * @see {@link https://www.npmjs.com/package/@codady/axui|NPM}
 * @issue QQ Group No.1:952502085
 * @copyright This software supports the MIT License, allowing free learning and commercial use, but please retain the terms 'ax,' 'axui,' 'AX,' and 'AXUI' within the software.
 * @license MIT license
 */

'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

const getComputedVar = (name) => getComputedStyle(document.documentElement).getPropertyValue(name).trim();

const prefix = getComputedVar(`--PREFIX`);

const alias = getComputedVar(`--ALIAS`);

const lang = {
    name: 'zh-CN',
    support: {
        content: '由于AXUI使用了<code>:has</code>的css伪类选择器，而您的浏览器相对陈旧，请更新至<code>Chrome105</code>以上内核的浏览器！',
        cancel: '下次再提醒我',
        confirm: '我知道了'
    },
    privacy: {
        content: '我们使用Cookie来确保您在我们的网站上获得最佳体验，并为您提供个性化服务。继续浏览即表示您同意我们的Cookie政策。',
        cancel: '拒绝',
        confirm: '接受'
    },
    ajax: {
        abort: `<i class="${prefix}c-warn">中止了请求！</i>`,
        timeout: `<i class="${prefix}c-error">请求超时了！</i>`,
        error: `<i class="${prefix}c-error">错误状态：{{this.status}}</i>`,
        submit: {
            btn: '正在提交',
            succ: '恭喜，提交成功！',
            fail: '对不起，提交失败！',
        }
    },
    more: {
        unfold: '收起',
        fold: '折叠',
    },
    button: {
        default: '新按钮',
        confirm: '确定',
        cancel: '取消',
        clear: '清除',
        close: '关闭',
        reset: '重置',
        submit: '提交',
        now: '现在',
        prev: '上一个',
        next: '下一个'
    },
    placehold: {
        note: '请写上备注内容！',
        fileName: '新文件',
        downloadName: '下载文件',
    },
    form: {
        placeholder: '请输入...',
        fileLabel: '请选择文件...',
        fileMulti: '{{this.data}}个文件：',
        maxLength: '最多可输入{{this.total}}个字符，已输入{{this.value}}个，还可以输入{{this.remaining}}个。',
        minLength: '至少输入{{this.min}}个字符，已输入{{this.value}}个，还要输入{{this.remaining}}个。',
        limitLength: '至少输入{{this.min}}个字符，最多可输入{{this.max}}个字符，已输入{{this.value}}个。',
        maxNumber: '最大取值{{this.max}}。',
        minNumber: '最小取值{{this.min}}。',
        limitNumber: '取值范围{{this.min}}~{{this.max}}。',
        exceed: '已超限。',
        range: '取值范围{{this.min}}~{{this.max}}。',
    },
    range: {
        result: `结果:{{this.multiple?this.range[0]+'-'+this.range[1]:this.value}}`,
    },
    valid: {
        regLocal: '\u4e00-\u9fa5',
        types: {
            'a': '小写字母',
            'A': '大写字母',
            'd': '数字',
            '~': '特殊字符',
            '@': '中文字符',
        },
        message: {
            wrongRule: '校验规则错误，请修正！',
            wrongFormat: '值格式错误，应该为文本格式！',
            noValids: '表单没有任何校验字段！',
        },
        strFormat: '值格式错误，应该为文本格式！',
        arrFormat: '参数格式错误，应该为数组格式！',
        succ: '{{ this.label || "" }}通过校验！',
        fail: '{{ this.label || "" }}校验失败！',
        required: '{{ this.label }}是必填项！',
        email: '{{ this.label }}请填写正确的邮箱！',
        cellphone: '{{ this.label }}请填写11位手机号！',
        landline: '{{ this.label }}请填写正确的座机号码！',
        ip: '{{ this.label }}请填写正确的IP地址！',
        id: '{{ this.label }}请填写正确的身份证号！',
        zip: '{{ this.label }}只能填写6位数字邮编！',
        url: '{{ this.label }}请填写正确的域名！',
        plate: '{{ this.label }}请填写正确的车牌号！',
        locale: '{{ this.label }}只能填写中文！',
        letter: '{{ this.label }}只能填写大小写英文字母！',
        string: '{{ this.label }}只能填写大小写英文字母和数字！',
        password: '{{ this.label }}只能填写大小写英文字母、数字以及特殊字符！',
        ymdhms: '{{ this.label }}只能填写类似2022-11-13 2:56:12的日期格式！',
        ymd: '{{ this.label }}只能填写类似2022-11-13的日期格式！',
        hms: '{{ this.label }}只能填写类似2:56:12的日期格式！',
        ym: '{{ this.label }}只能填写类似2022-11的日期格式！',
        y: '{{ this.label }}只能填写4位数字年份！',
        m: '{{ this.label }}只能填写1~12月份！',
        d: '{{ this.label }}只能填写1~31日！',
        date: '{{ this.label }}请填写有效的日期！',
        integer: '{{ this.label }}只能填写非0开头的正整数！',
        number: '{{ this.label }}只能填写数字，包括正数、负数、整数、小数！',
        'date=': '{{ this.label }}只能是{{ this.data }}！',
        'date>': '{{ this.label }}需超过{{ this.data }}！',
        'date>=': '{{ this.label }}不可早于{{ this.data }}！',
        'date<': '{{ this.label }}不可超过{{ this.data }}！',
        'date<=': '{{ this.label }}不可晚于{{ this.data }}！',
        'date><': '{{ this.label }}需超过{{ this.data[0] }}，且不可超过{{ this.data[1] }}！',
        'date><=': '{{ this.label }}需超过{{ this.data[0] }}，且不可超过或等于{{ this.data[1] }}！',
        'date>=<': '{{ this.label }}需超过或等于{{ this.data[0] }}，且不可超过{{ this.data[1] }}！',
        'date>=<=': '{{ this.label }}不可早于{{ this.data[0] }}，且不可晚于{{ this.data[1] }}！',
        'than=': '{{ this.label }}需要等于{{ this.data }}！',
        'than>': '{{ this.label }}需要大于{{ this.data }}！',
        'than>=': '{{ this.label }}需要大于或等于{{ this.data }}！',
        'than<': '{{ this.label }}需要小于{{ this.data }}！',
        'than<=': '{{ this.label }}需要小于或等于{{ this.data }}！',
        'than><': '{{ this.label }}需要大于{{ this.data[0] }}，且小于{{ this.data[1] }}！',
        'than><=': '{{ this.label }}需要大于{{ this.data[0] }}个，且小于等于{{ this.data[1] }}！',
        'than>=<': '{{ this.label }}需要大于等于{{ this.data[0] }}个，且小于{{ this.data[1] }}！',
        'than>=<=': '{{ this.label }}需要大于等于{{ this.data[0] }}个，且小于等于{{ this.data[1] }}！',
        'length=': '{{ this.label }}已输入{{ this.value.length }}个字符，只能填写{{ this.data }}个字符！',
        'length>': '{{ this.label }}已输入{{ this.value.length }}个字符，字符数量需多于{{ this.data }}个！',
        'length>=': '{{ this.label }}已输入{{ this.value.length }}个字符，字符数量不可少于{{ this.data }}个！',
        'length<': '{{ this.label }}已输入{{ this.value.length }}个字符，字符数量需少于{{ this.data }}个！',
        'length<=': '{{ this.label }}已输入{{ this.value.length }}个字符，字符数量不可多于{{ this.data }}个！',
        'length><': '{{ this.label }}已输入{{ this.value.length }}个字符，字符数量需多于{{ this.data[0] }}个，且少于{{ this.data[1] }}个！',
        'length><=': '{{ this.label }}已输入{{ this.value.length }}个字符，字符数量需多于{{ this.data[0] }}个，且少于或等于{{ this.data[1] }}个！',
        'length>=<': '{{ this.label }}已输入{{ this.value.length }}个字符，字符数量需多于或等于{{ this.data[0] }}个，且少于{{ this.data[1] }}个！',
        'length>=<=': '{{ this.label }}已输入{{ this.value.length }}个字符，字符数量不可少于{{ this.data[0] }}个，且不可多于{{ this.data[1] }}个！',
        'count=': '{{ this.label }}有{{ this.value }}项，必须且只能选择{{ this.data }}项！',
        'count>': '{{ this.label }}有{{ this.value }}项，选择项需要多于{{ this.data }}！',
        'count>=': '{{ this.label }}有{{ this.value }}项，至少选择{{ this.data }}项！',
        'count<': '{{ this.label }}有{{ this.value }}项，选择项需要少于{{ this.data }}！',
        'count<=': '{{ this.label }}有{{ this.value }}项，最多选择{{ this.data }}项！',
        'count><': '{{ this.label }}有{{ this.value }}项，选择项需多于{{ this.data[0] }}，且少于{{ this.data[1] }}！',
        'count><=': '{{ this.label }}有{{ this.value }}项，选择项需多于{{ this.data[0] }}，且少于或等于{{ this.data[1] }}！',
        'count>=<': '{{ this.label }}有{{ this.value }}项，选择项需多于或等于{{ this.data[0] }}，且少于{{ this.data[1] }}！',
        'count>=<=': '{{ this.label }}有{{ this.value }}项，至少选择{{ this.data[0] }}项，且不能多于{{ this.data[1] }}项！',
        include: '{{ this.label }}的值应该在"{{ this.data }}"之中！',
        exclude: '{{ this.label }}的值不能在"{{ this.data }}"之中！',
        same: '{{ this.label }}字段值与"{{ this.data[1] || this.data[0] }}"字段值不一致！',
        different: '{{ this.label }}字段值不能与"{{ this.data[1] || this.data[0] }}"字段值一致！',
        strength: '{{ this.label }}的当前强度为{{ this.value}}，要求达到{{ this.data }}！',
        specific: `{{ this.label }}要求{{ for(let k in this.data){/}}{{k+'至少'+this.data[k]+'个'}}{{ (Object.keys(this.data).slice(-1)[0] !== k)? '，':''}}{{}/}}！`,
        combine: `{{ this.label }}要求{{ this.data.types.join('、') }}至少{{ this.data.total }}种！`,
    },
    status: {
        warn: '有警告',
        succ: '完成了',
        error: '有报错',
        issue: '有疑问',
        info: '有消息',
        confirm: '已确认',
        cancel: '已取消',
        forbid: '已禁用'
    },
    message: {
        heading: {
            warn: '操作警告！',
            succ: '操作成功！',
            error: '操作失败！',
            issue: '操作疑问！',
            info: '信息提示！',
        },
        content: {
            warn: '警告！运行过程中可能存在故障，请注意排查！',
            succ: '恭喜！运行顺利或者操作成功！',
            error: '失败！运行过程中发生了错误或操作失败！',
            issue: '有疑问！运行过程中遇到一些问题需要解决！',
            info: '提示！运行中未出现状况，请继续！',
        },
    },
    tree: {
        label: '新分支',
        title: {
            folder: '新增枝干分支',
            file: '新增叶子分支',
            edit: '编辑分支',
            remove: '删除分支',
            arrow: '点击折叠或展开',
        },
        message: {
            remove: '确定要删除"{{this.label}}"分支么',
        },
        paginated: {
            more: '查看更多',
            next: '下一页',
            first: '返回首页',
            info: '"{{this.label}}"还剩{{this.rest}}条信息',
            main: '主分支',
        },
        result: `<i ${alias}="holder">还未选择...</i>`,
    },
    accordion: {
        label: '新板块',
        content: '新内容',
        extra: '更多内容',
        title: {
            add: '新增板块',
            edit: '编辑板块',
            remove: '删除板块',
            arrow: '点击折叠或展开',
        },
        message: {
            remove: '确定要删除"{{this.label}}"板块么',
        },
    },
    tab: {
        label: '新标签',
        content: '新内容',
        title: {
            add: '新增页签',
            edit: '编辑页签',
            close: '删除页签',
            move: '移动页签',
            update: '更新页签',
        },
        message: {
            add: '确定要新增"{{this.label}}"页签么',
            edit: '确定要编辑"{{this.label}}"页签么',
            close: '确定要删除"{{this.label}}"页签么',
            move: '确定要移动"{{this.label}}"页签么',
            update: '确定要更新"{{this.label}}"页签么',
        },
    },
    flat: {
        label: '新项目'
    },
    spy: {
        isObserved: `媒体文件{{ this.src }}已经处于监听状态，不需要添加监听操作！`,
        isUnobserved: `媒体文件{{ this.src }}还未被监听，不需要取消监听操作！`
    },
    tags: {
        emptyholder: '还没有创建标签！',
        placeholder: '请输入...',
        includePart: '包含了重复的标签!',
        includeFull: '标签完全重复，添加失败!',
    },
    retrieval: {
        status: `共有<u>{{this.value}}</u>个结果符合<s>{{this.keys}}</s>要求！`,
        nullKeys: `没有检索词且没有检索结果！`,
    },
    drag: {
        holderDrag: '转移中...',
        holderDrop: '释放到这里...',
    },
    progress: {
        complete: '已完成!',
        tips: '当前进度',
    },
    infinite: {
        finish: '没有更多内容了',
        error: '请求终止，已停止加载',
        next: '<ax-btn width="x5">查看更多</ax-btn>',
        preload: '等待加载数据',
        loading: '正在加载数据',
        loaded: '单页数据加载完成!',
    },
    virtualize: {
        preload: '等待加载数据',
    },
    pagination: {
        first: '首页',
        last: '尾页',
        prev: '上一页',
        next: '下一页',
        ellipsis: '...',
        tips: '{{this.current}}/{{this.pages}}',
        total: '共有{{this.total}}条数据',
        locate: '跳到',
        count: '每页',
        page: '页',
        unit: '条',
    },
    datetime: {
        month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
        week: ['一', '二', '三', '四', '五', '六', '日'],
        weeks: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
        year: {
            prev: '上一页',
            next: '下一页',
            placeholder: '输入年份',
        },
        range: {
            hyphen: '至',
            checkbox: '选择同一天'
        },
        unit: {
            Y: '年',
            M: '月',
            D: '日',
            W: '周',
            h: '时',
            m: '分',
            s: '秒',
        },
        bc: '公元前',
        daytime: {
            select: '时间选择',
            start: '开始时间',
            end: '结束时间'
        },
        toolTip: {
            restore: '还原初始值',
            reset: '归零',
            now: '设为当前时间',
            close: '关闭时间选择器',
        },
        empty: `<i class="${prefix}c-ignore">还未选择日期!</i>`,
        message: {
            requireTwoValue: '区间模式至少需要选择两个日期！',
            requireYearFormat: '请填入正确的年份格式!',
            requireOneSelected: '请至少选择一个日期!',
        },
        noEvent: `<i class="${prefix}c-ignore">今天没有需要安排的事项!</i>`,
    },
    rate: {
        title: {
            dft: '暂无评星',
            clear: '评星归零',
        },
        template: {
            result: `{{this.stars}}星`,
            tooltip: `{{this.stars}}星,总分:{{this.value}}`,
        },
        star: '星',
    },
    editor: {
        defer: '点击加载内容',
        placeholder: '请输入...',
        path: '路径:',
        chars: '文字:',
        paras: '段落:',
        fontsizeDft: '默认字号',
        alignDft: '默认排列',
        tagsDft: '特殊标签',
        tips: {
            bold: '加粗',
            italic: '斜体',
            through: '删除线',
            underline: '下划线',
            alignDft: '默认对齐',
            alignLeft: '左对齐',
            alignRight: '右对齐',
            alignCenter: '居中对齐',
            alignJustify: '两端对齐',
            indentMore: '增加缩进',
            indentLess: '减少缩进',
            sub: '下标',
            sup: '上标',
            highlight: '高亮(MARK)',
            em: '强调(EM)',
            ruby: '拼音(RUBY)',
            rt: '拼音(RT)',
            address: '地址(ADDRESS)',
            time: '时间(TIME)',
            blockquote: '段落引用(BLOCKQUOTE)',
            cite: '行内引用(CITE)',
            codeInline: '行内代码(CODE)',
            codeBlock: '代码块(PRE+CODE)',
            source: '源码模式',
            heading: '设置标题',
            hr: '插入水平线(HR)',
            br: '插入换行符(BR)',
            p: '插入段落符(P)',
            listOl: '有序列表(OL+LI)',
            listUl: '无序列表(UL+LI)',
            listCheck: '任务列表(CHECKBOX)',
            paragraph: '插入段落(DIV+BR)',
            fontSet: '文字设置',
            fontSize: '字号大小',
            fontColor: '文字颜色',
            fontBg: '文字背景色',
            h1: '一号标题',
            h2: '二号标题',
            h3: '三号标题',
            h4: '四号标题',
            h5: '五号标题',
            h6: '六号标题',
            text: '正文',
        }
    },
    select: {
        placeholder: '请选择...',
        title: {
            close: '清空'
        },
        search: {
            fail: `没有找到符合"{{this.keys}}"的选项`,
            succ: `找到了{{this.value}}项符合"{{this.keys}}"`,
            start: '还没有输入检索关键字',
            placeholder: '请输入关键字...',
        },
        check: {
            ed: '未勾选,选择全部',
            ing: '勾选了部分,选择全部',
            none: '已勾选全部,取消全部'
        },
        stats: `已选择了{{this.value}}/{{this.total}}项`,
    },
    upload: {
        paste: {
            before: '点击这里粘贴上传',
            ing: '请使用ctrl+v组合键',
            after: '完成了粘贴!',
        },
        tips: {
            suffix: `支持{{this.value}}格式`,
            size: `单文件子节数不超过{{this.value}}MB`,
            min: `至少上传{{this.value}}个文件`,
            max: `最多上传{{this.value}}个文件`,
            free: '上传文件未做限制',
        },
        progress: {
            passed: '文件合格',
            notPassed: '文件不合格',
            rendered: '等待上传',
            uploading: '上传中',
            uploaded: '已上传',
            received: '已接收',
            getAuth: '获取授权中',
            authorized: '已授权',
            unauthorized: '未获得授权',
            failed: '上传失败',
        },
        summary: `提交了{{this.total}}个文件,成功上传了{{this.count}}个,共{{this.size}}`,
        message: {
            single: {
                passed: '通过校验!',
                max: '文件数量太多,请删除!',
                size: '文件体积太大,请删除!',
                suffix: '文件格式错误,请删除!',
                success: '上传成功!',
                failed: '提交地址可能错误,请删除!',
            },
            global: {
                passed: '所有文件通过校验!',
                min: '请至少上传{{this.value}}个文件!',
                max: '最多只能上传{{this.value}}个文件,可先删除再添加!',
            }
        },
        button: {
            choose: '选择文件',
            upload: '批量上传',
            clear: '批量删除',
            gallery: '点击或拖拽上传',
            picture: '选择文件',
        },
        thead: ['图示', '文件名', '文件体积', '上传进度', '实时消息', '上传状态', '操作'],
    },
    confirm: {
        heading: '',
    },
    twilight: {
        day: '白天',
        night: '黑夜',
    },
};

const config = {
    initial: true,
    support: false,
    privacy: false,
    lang,
    attrs: {
        ajaxSpin: `spinning`,
        ajaxState: `ajax`,
    },
    debounce: 200,
    throttle: 500,
    rootStart: -1,
    idStart: 0,
    floorStart: 0,
    pathHyphen: '~',
    rangeHyphen: '~',
    labelHyphen: '/',
    splitHyphen: ',',
    wordHyphen: ' ',
    actClass: `${prefix}opened`,
    reqProp: 'REQRETRY',
    parser: 'new Function',
    warn: {
        init: 'The initialization process of the instance has been stopped. You will need to manually initialize it using the init() method later!',
        emptyCont: 'Data was not obtained, but execution was not halted!',
    },
    error: {
        parse: 'Getting data from HTML resulted in an error, an empty array was returned, but execution was not interrupted!',
    },
    message: {},
    valid: {
        regChars: '~!@#$%^&*',
        lengthStr: 6,
    },
    popup: {},
    alert: {},
    more: {},
    menu: {},
    tree: {},
    drawer: {},
};

const isNull = (data) => [undefined, null, 'undefined', 'null'].includes(data);

const augment = function (arg) {
    if (isNull(arg) || !arg.name)
        return;
    let target;
    if (!arg.target || arg.target === 'ax') {
        target = this;
    }
    else {
        for (let k in this) {
            if (this[k].name === arg.target) {
                target = this[k];
                break;
            }
        }
        if (!target)
            throw new Error(`Cannot find the ${arg.target} property in the ax object!`);
    }
    if (arg.type === 'method') {
        target[target.prototype ? 'prototype' : '__proto__'][arg.name] = arg.data;
    }
    else {
        Reflect.set(target, arg.name, arg.data);
    }
};

const getDataType = (obj) => {
    let tmp = Object.prototype.toString.call(obj).slice(8, -1), result;
    if (tmp === 'Function' && /^\s*class\s+/.test(obj.toString())) {
        result = 'Class';
    }
    else if (tmp === 'Object' && Object.getPrototypeOf(obj) !== Object.prototype) {
        result = 'Instance';
    }
    else {
        result = tmp;
    }
    return result;
};

const isEmpty = (data) => {
    let type = getDataType(data), flag;
    if (!data) {
        flag = true;
    }
    else {
        flag = (type === 'Object') ? (Object.keys(data).length === 0) :
            (type === 'Array') ? data.join('') === '' :
                (type === 'Function') ? (data.toString().replace(/\s+/g, '').match(/{.*}/g)[0] === '{}') :
                    (type === 'Symbol') ? (data.toString().replace(/\s+/g, '').match(/\(.*\)/g)[0] === '()') : false;
    }
    return flag;
};

const getEl = (obj, wrap) => {
    let objType = getDataType(obj), parType = getDataType(wrap), parent = parType.includes('HTML') ? wrap : document.querySelector(wrap), result = null;
    if (obj) {
        if (objType.includes('HTML')) {
            result = obj;
        }
        else if (objType === 'String') {
            try {
                result = (parent || document).querySelector(obj.trim());
            }
            catch {
                result = null;
            }
        }
    }
    return result;
};

const deepClone = (data) => {
    let dataType = getDataType(data), result;
    if (dataType === 'Object') {
        let newObj = {}, symbols = Object.getOwnPropertySymbols(data);
        for (let k in data) {
            newObj[k] = deepClone(data[k]);
        }
        if (symbols.length > 0) {
            for (let k of symbols) {
                newObj[k] = deepClone(data[k]);
            }
        }
        result = newObj;
    }
    else if (dataType === 'Array') {
        result = data.map((k) => deepClone(k));
    }
    else if (dataType === 'Date') {
        result = new Date(data);
    }
    else {
        result = data;
    }
    return result;
};

const deepMerge = (target, source, opt) => {
    let targetType = getDataType(target), sourceType = getDataType(source), options = Object.assign({ arrAppend: false, propAppend: true, targetClone: false, override: 'partial' }, opt), result = options.targetClone ? deepClone(target) : target;
    if (targetType !== 'Object' || sourceType !== 'Object') {
        return result;
    }
    for (let k in source) {
        if (source.hasOwnProperty(k) && result.hasOwnProperty(k)) {
            let _resultType = getDataType(result[k]), _sourceType = getDataType(source[k]);
            if (_resultType !== _sourceType) {
                if (options.override === 'partial' && result.hasOwnProperty(k) && result[k]?.hasOwnProperty('enable') && typeof source[k] === 'boolean') {
                    if (result[k]?.hasOwnProperty('enable') && typeof source[k] === 'boolean') {
                        result[k].enable = source[k];
                    }
                    else if (source[k]?.hasOwnProperty('enable') && typeof result[k] === 'boolean') {
                        result = Object.assign({ enable: result[k] }, source[k]);
                    }
                    else {
                        result[k] = source[k];
                    }
                }
                else {
                    result[k] = source[k];
                }
            }
            else {
                if (_sourceType === 'Object') {
                    result[k] = deepMerge(result[k], source[k], options);
                }
                else if (_sourceType === 'Array' && options.arrAppend) {
                    result[k].push(...source[k]);
                }
                else {
                    result[k] = source[k];
                }
            }
        }
        else if (source.hasOwnProperty(k) && !result.hasOwnProperty(k) && options.propAppend) {
            result[k] = source[k];
        }
    }
    let symbols = Object.getOwnPropertySymbols(source);
    if (symbols.length > 0) {
        for (let k of symbols) {
            result[k] = source[k];
        }
    }
    return result;
};

const requireTypes = (data, require, cb) => {
    let type = getDataType(data).toLowerCase(), types = typeof require === 'string' ? [require] : require;
    type.includes('html') ? type = 'element' : null;
    types = types.map((k) => k.toLowerCase());
    if (cb) {
        try {
            if (!types.includes(type)) {
                throw new Error(`Wrong data type,Require types: "${'' + types}"!`);
            }
        }
        catch (error) {
            cb(error);
        }
    }
    else {
        if (!types.includes(type)) {
            throw new Error(`Wrong data type,Require types: "${'' + types}"!`);
        }
    }
};

const parseStr = ({ content = '', type = 'object', method = config.parser, catchable = false, error }) => {
    let dft = {
        start: type === 'object' ? '{' : '[',
        end: type === 'object' ? '}' : ']',
        return: type === 'object' ? {} : type === 'array' ? [] : null,
    }, result = dft.return;
    if (!content)
        return dft.return;
    let trim = content.trim();
    if (['object', 'array'].includes(type)) {
        if (!trim.startsWith(dft.start) || !trim.endsWith(dft.end))
            return result;
    }
    try {
        let tmp = typeof method === 'function' ? method(trim) : method === 'JSON.parse' ? JSON.parse(trim) : new Function(`"use strict"; return ${trim}`)();
        result = tmp;
    }
    catch (err) {
        error && error(err);
        if (catchable)
            throw err;
    }
    return result;
};

const strToJson = (str, type = 'object') => {
    let dft = type === 'array' ? [] : {};
    if (typeof str !== 'string')
        return dft;
    str = str.trim();
    if (!str)
        return dft;
    str = (str.startsWith('[') && str.endsWith(']')) || (str.startsWith('{') && str.endsWith('}')) ? str : `{${str}}`;
    try {
        return parseStr({
            content: str,
            type,
            catchable: true,
        });
    }
    catch {
        return dft;
    }
};

const attrToJson = (elem, attr) => {
    requireTypes(attr, 'string');
    let el = getEl(elem), elAttr = el.getAttribute(attr), result = {};
    if (el && attr && elAttr) {
        result = strToJson(elAttr);
    }
    return result;
};

const extend = ({ target = {}, source = {}, host = null, attr = '' }) => {
    let targetType = getDataType(target), el = getEl(host);
    if (targetType !== 'Object') {
        return target;
    }
    else {
        source && deepMerge(target, source);
        el && attr && deepMerge(target, attrToJson(el, attr));
    }
    return target;
};

const ax = {
    frame: 0,
    ajaxStorage: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    prefix,
    alias,
    compSign: 'comp',
    embedSign: 'embed',
    namePfx: 'TMP_',
    messages: [],
    valids: [],
    config,
    augment,
    tasks: [],
    install(vue, options) {
        !isEmpty(options) && extend({
            target: this.config,
            source: options,
        });
        vue.config.globalProperties.$ax = this;
    }
};

const fieldTypes = ['input', 'file', 'textarea', 'range', 'number', 'datetime', 'upload', 'select', 'radio', 'checkbox', 'radios', 'checkboxes'];

const renderTpl = (html, data) => {
    requireTypes(html, 'string');
    requireTypes(data, ['array', 'object']);
    if (!html || Object.keys(data).length === 0) {
        return '';
    }
    let regStart = '\\{\\{', regEnd = '\\}\\}', exeEnd = '/', tplReg = new RegExp(`${regStart}([\\s\\S]+?)?${regEnd}`, 'g'), code = '"use strict";let str=[];\n', cursor = 0, match, result = '', add = (fragment, isJs) => {
        isJs ? (code += (fragment.endsWith(exeEnd) ? fragment.replace('=&gt;', '=>').slice(0, -1) + '\n' : 'str.push(' + fragment + ');\n'))
            : (code += (fragment !== '' ? 'str.push("' + fragment.replace(/"/g, '\\"') + '");\n' : ''));
        return add;
    };
    while (match = tplReg.exec(html)) {
        add(html.slice(cursor, match.index))(match[1], true);
        cursor = match.index + match[0].length;
    }
    add(html.slice(cursor));
    code += `return str.join('');`;
    try {
        result = new Function(code.replace(/[\r\t\n]/g, '')).apply(data);
    }
    catch (err) {
        console.error(`'${err.message}'`, ' in \n', code, '\n');
    }
    return result;
};

const getScreenSize = () => getComputedVar(`--SCREEN`);

const startUpper = (str) => {
    str = str.trim();
    return str.slice(0, 1).toUpperCase() + str.slice(1);
};

const sliceStrEnd = ({ str = '', key = '#', type = 'afterend', contain = true }) => {
    str = str.toString();
    key = key.toString();
    let result = '', indexKey = 0, lenKey = key.length, lenEnd = str.length, indexStart = 0;
    if (!str || !key) {
        return result;
    }
    str = str.trim();
    if (str.includes(key)) {
        if (type === 'beforebegin') {
            indexKey = str.indexOf(key);
            contain ? indexKey += lenKey : null;
            lenEnd = indexKey;
        }
        else if (type === 'afterbegin') {
            indexKey = str.indexOf(key);
            !contain ? indexKey += lenKey : null;
            indexStart = indexKey;
        }
        else if (type === 'beforeend') {
            indexKey = str.lastIndexOf(key);
            contain ? indexKey += lenKey : null;
            lenEnd = indexKey;
        }
        else if (type === 'afterend') {
            indexKey = str.lastIndexOf(key);
            !contain ? indexKey += lenKey : null;
            indexStart = indexKey;
        }
        result = str.substring(indexStart, lenEnd);
    }
    return result;
};

const delay = function ({ duration = 2000, todo, doing, done, frame = 0 }) {
    if (duration < 0)
        Promise.reject(new Error('Invalid duration'));
    todo && todo(frame);
    return new Promise((resolve, reject) => {
        try {
            if (!duration) {
                done && done(frame);
                resolve(frame);
            }
            let deadline = Date.now() + duration, listen = () => {
                let newTime = Date.now();
                if (newTime >= deadline) {
                    cancelAnimationFrame(frame);
                    done && done(frame);
                    resolve(frame);
                    frame = 0;
                }
                else {
                    frame = requestAnimationFrame(listen);
                    doing && doing(frame);
                }
            };
            listen();
        }
        catch (e) {
            reject(e);
        }
    });
};

const getPlaces = (data) => data.toString().split(".")[1]?.length || 0;

const toNumber = (data, opt) => {
    let result = 0, number = Number(data), options = Object.assign({ places: 10, mode: 'round', zero: false, epsilon: true }, opt);
    if (!data || !number) {
        return 0;
    }
    if (options.places < 0) {
        return number;
    }
    else {
        let precise = number + (options.epsilon ? Number.EPSILON : 0), tail = Number(`1${'0'.repeat(options.places)}`), tempPrecise = precise * tail;
        if (options.mode === 'floor') {
            result = Math.floor(tempPrecise) / tail;
        }
        else if (options.mode === 'ceil') {
            result = Math.ceil(tempPrecise) / tail;
        }
        else {
            result = Math.round(tempPrecise) / tail;
        }
        if (options.zero) {
            let decPlaces = getPlaces(result);
            options.places > decPlaces ? result = result + (!decPlaces ? '.' : '') + ('0'.repeat(options.places - decPlaces)) : null;
        }
        return result;
    }
};

const toPixel = (data, multiple) => {
    let result = 0;
    if (!data) {
        return result;
    }
    multiple = multiple || parseInt(getComputedVar(`--${prefix}fs-base`)) || 10;
    if (typeof data === 'string') {
        data = data.trim();
        if (data.endsWith('rem') || data.endsWith('REM')) {
            result = ~~(toNumber(data.replace('rem', '').replace('REM', '')) * multiple);
        }
        else if (data.endsWith('px') || data.endsWith('PX')) {
            result = ~~toNumber(data.replace('px', '').replace('PX', ''));
        }
        else {
            result = ~~data;
        }
    }
    else if (typeof data === 'number') {
        result = ~~data;
    }
    return result;
};

const preventDft = (event, enhance = false) => {
    event.cancelable && event.preventDefault();
    enhance && event.stopPropagation();
};

const isMobi = ('ontouchstart' in document.documentElement);

const events = isMobi ? ['touchstart', 'touchmove', 'touchend', 'touchcancel'] : ['mousedown', 'mousemove', 'mouseup', 'mouseleave'];

const icons = {
    font: {
        succ: `<i class="${prefix}icon-check-o"></i>`,
        error: `<i class="${prefix}icon-close-o"></i>`,
        warn: `<i class="${prefix}icon-warn-o"></i>`,
        info: `<i class="${prefix}icon-info-o"></i>`,
        issue: `<i class="${prefix}icon-issue-o"></i>`,
        'succ-t': `<i class="${prefix}icon-check-o-t"></i>`,
        'error-t': `<i class="${prefix}icon-close-o-t"></i>`,
        'warn-t': `<i class="${prefix}icon-warn-o-t"></i>`,
        'info-t': `<i class="${prefix}icon-info-o-t"></i>`,
        'issue-t': `<i class="${prefix}icon-issue-o-t"></i>`,
        'succ-f': `<i class="${prefix}icon-check-o-f"></i>`,
        'error-f': `<i class="${prefix}icon-close-o-f"></i>`,
        'warn-f': `<i class="${prefix}icon-warn-o-f"></i>`,
        'info-f': `<i class="${prefix}icon-info-o-f"></i>`,
        'issue-f': `<i class="${prefix}icon-issue-o-f"></i>`,
    },
    svg: {
        succ: `<svg class="${prefix}svg-succ" xmlns="http://www.w3.org/2000/svg" width="86.6986mm" height="86.6986mm" viewBox="0 0 86.6986 86.6986"><path class="${prefix}line ${prefix}bg" d="M7.238500000000002,43.3493A36.1108,36.1108 0,1,1 79.4601,43.3493A36.1108,36.1108 0,1,1 7.238500000000002,43.3493"></path><path class="${prefix}line ${prefix}out" d="M7.238500000000002,43.3493A36.1108,36.1108 0,1,1 79.4601,43.3493A36.1108,36.1108 0,1,1 7.238500000000002,43.3493"></path><path class="${prefix}line ${prefix}in-1" d="M26.316,42.859L37.9984,54.5414L60.3826,32.1572"></path></svg>`,
        error: `<svg class="${prefix}svg-error" xmlns="http://www.w3.org/2000/svg" width="86.6986mm" height="86.6986mm" viewBox="0 0 86.6986 86.6986"><path class="${prefix}line ${prefix}bg" d="M7.238500000000002,43.3493A36.1108,36.1108 0,1,1 79.4601,43.3493A36.1108,36.1108 0,1,1 7.238500000000002,43.3493"></path><path class="${prefix}line ${prefix}out" d="M7.238500000000002,43.3493A36.1108,36.1108 0,1,1 79.4601,43.3493A36.1108,36.1108 0,1,1 7.238500000000002,43.3493"></path><path class="${prefix}line ${prefix}in-1" d="M28.774,57.9246L57.9247,28.7739"></path><path class="${prefix}line ${prefix}in-2" d="M57.9246,57.9246L28.7739,28.7739"></path></svg>`,
        warn: `<svg class="${prefix}svg-warn" xmlns="http://www.w3.org/2000/svg" width="86.6986mm" height="86.6986mm" viewBox="0 0 86.6986 86.6986"><path class="${prefix}line ${prefix}bg" d="M43.4611 7.24c2.8081,0.0924 4.39,1.7 5.3045,3.1159l17.4543 29.9414 17.3445 29.7538c0.5448,1.0193 1.596,4.0544 0.1109,6.4168 -1.4849,2.3626 -3.6815,2.9155 -5.3768,2.992l-34.9082 0.0002 -34.6892 -0.0002c-1.1636,-0.0421 -4.3433,-0.6583 -5.6666,-3.1131 -1.3232,-2.4549 -0.7085,-4.6157 0.0723,-6.1078l17.454 -29.9417 17.3449 -29.7537c0.6185,-0.977 2.7471,-3.396 5.5554,-3.3036z"></path><path class="${prefix}line ${prefix}out" d="M43.4611 7.24c2.8081,0.0924 4.39,1.7 5.3045,3.1159l17.4543 29.9414 17.3445 29.7538c0.5448,1.0193 1.596,4.0544 0.1109,6.4168 -1.4849,2.3626 -3.6815,2.9155 -5.3768,2.992l-34.9082 0.0002 -34.6892 -0.0002c-1.1636,-0.0421 -4.3433,-0.6583 -5.6666,-3.1131 -1.3232,-2.4549 -0.7085,-4.6157 0.0723,-6.1078l17.454 -29.9417 17.3449 -29.7537c0.6185,-0.977 2.7471,-3.396 5.5554,-3.3036z"></path><path class="${prefix}line ${prefix}in-1" d="M43.3493,27.8713L43.3493,57.2858"></path><circle class="${prefix}circle ${prefix}in-2" cx="43.3492" cy="64.3337" r="2.1166"></circle></svg>`,
        info: `<svg class="${prefix}svg-info" xmlns="http://www.w3.org/2000/svg" width="86.6986mm" height="86.6986mm" viewBox="0 0 86.6986 86.6986"><path class="${prefix}line ${prefix}bg" d="M7.238500000000002,43.3493A36.1108,36.1108 0,1,1 79.4601,43.3493A36.1108,36.1108 0,1,1 7.238500000000002,43.3493"></path><path class="${prefix}line ${prefix}out" d="M7.238500000000002,43.3493A36.1108,36.1108 0,1,1 79.4601,43.3493A36.1108,36.1108 0,1,1 7.238500000000002,43.3493"></path><path class="${prefix}line ${prefix}in-1" d="M43.3493,65.0602L43.3493,30.9723"></path><circle class="${prefix}circle ${prefix}in-2" cx="43.3492" cy="23.5856" r="2.1166"></circle></svg>`,
        issue: `<svg class="${prefix}svg-issue" xmlns="http://www.w3.org/2000/svg" width="86.6986mm" height="86.6986mm" viewBox="0 0 86.6986 86.6986"><path class="${prefix}line ${prefix}bg" d="M7.238500000000002,43.3493A36.1108,36.1108 0,1,1 79.4601,43.3493A36.1108,36.1108 0,1,1 7.238500000000002,43.3493"></path><path class="${prefix}line ${prefix}out" d="M7.238500000000002,43.3493A36.1108,36.1108 0,1,1 79.4601,43.3493A36.1108,36.1108 0,1,1 7.238500000000002,43.3493"></path><path class="${prefix}line ${prefix}in-1" d="M32.3757 35.7255c-0.2203,-11.823 12.5789,-14.1087 18.4056,-9.4189 5.4663,4.3995 4.7426,12.804 -3.1088,17.9938 -3.0015,1.9839 -3.0003,3.8403 -3.0003,10.1707"></path><circle class="${prefix}circle ${prefix}in-2" cx="44.6612" cy="60.5502" r="2.1166"></circle></svg>`,
    }
};

const getFullGap = () => getComputedVar(`--${prefix}g-full`);

const propsMap = {
    x: { axis: 'x', position: 'left', overflow: 'overflowX', inner: 'clientWidth', outer: 'offsetWidth', scroll: 'scrollLeft', client: 'clientX', size: 'width', index: 4, offset: 'offsetLeft', gap: 'marginLeft' },
    y: { axis: 'y', position: 'top', overflow: 'overflowY', inner: 'clientHeight', outer: 'offsetHeight', scroll: 'scrollTop', client: 'clientY', size: 'height', index: 5, offset: 'offsetTop', gap: 'marginTop' }
};

const instance = {
    
    data: [],
    destroyFun: (item) => {
        if (!item) {
            return false;
        }
        if ((!item.ins.hasOwnProperty('destroyed') || !item.ins.destroyed) && item.ins.__proto__.destroy) {
            item.ins.destroy();
            item.destTime = Date.now();
        }
    },
    initFun: (item) => {
        if (!item) {
            return false;
        }
        if ((!item.ins.hasOwnProperty('destroyed') || item.ins.destroyed) && item.ins.__proto__.init) {
            item.ins.init();
            item.initTime = Date.now();
        }
    },
    
    push: function (ins, name = '', type = '') {
        if (!ins) {
            return false;
        }
        let obj = { name, ins, type, pushTime: Date.now() };
        if (!this.data.some((k) => k.ins === ins)) {
            this.data.push(obj);
        }
        return this;
    },
    
    find: function (name, type = '', destroyed = false) {
        if (!name) {
            return null;
        }
        let item;
        item = this.data.find((k) => {
            let flag = type ? k.type === type : true;
            return (k.ins.hasOwnProperty('destroyed')) ? (k.name === name && k.ins.destroyed === destroyed && flag) : (k.name === name && flag);
        });
        return item ? item.ins : null;
    },
    
    findAll: function (type = '', destroyed = false) {
        let items = [];
        if (!type) {
            items = this.data.filter((i) => i.ins.destroyed === destroyed);
        }
        else {
            items = this.data.filter((i) => {
                return (i.ins.hasOwnProperty('destroyed')) ? (i.type === type && i.ins.destroyed === destroyed) : (i.type === type);
            });
        }
        return items.length > 0 ? items.map((i) => i.ins) : [];
    },
    
    destroy: function (name, type) {
        if (!name) {
            return false;
        }
        let item = type ? (this.data.find((i) => i.name === name && i.type === type)) : (this.data.find((i) => i.name === name));
        if (item) {
            this.destroyFun(item);
        }
        return this;
    },
    
    destroyAll: function (type) {
        let items = !type ? this.data : this.data.filter((i) => i.type === type);
        items.forEach((i) => {
            this.destroyFun(i);
        });
        return this;
    },
    
    clear: function () {
        this.data.forEach((i) => {
            this.destroyFun(i);
        });
        this.data.length = 0;
        return this;
    },
    
    init: function (name, type) {
        if (!name) {
            return false;
        }
        let item = type ? (this.data.find((i) => i.name === name && i.type === type)) : (this.data.find((i) => i.name === name));
        if (item) {
            this.initFun(item);
        }
        return this;
    },
    
    initAll: function (type) {
        let items = !type ? this.data : this.data.filter((i) => i.type === type);
        items.forEach((i) => {
            this.initFun(i);
        });
        return this;
    }
};

const getEls = (data, parent) => {
    let type = getDataType(data), parentEl = getEl(parent) || document, result = [];
    if (isEmpty(data)) {
        return result;
    }
    if (type.includes('HTML')) {
        result.push(data);
    }
    else if (type === 'String') {
        data = data.trim();
        result = data.split(',').map((k) => {
            return [...parentEl.querySelectorAll(k)];
        }).flat();
    }
    else if (type === 'Array') {
        result = data.map((k) => {
            return getEl(k, parentEl);
        });
    }
    return result.filter(Boolean);
};

const createEl = (name, attrs, content) => {
    name = name || 'div';
    let rootName = name.toUpperCase().trim(), rootEl = document.createElement(rootName), attrsType = getDataType(attrs), loop = (host, data) => {
        if (data === '' || data === null || data === undefined) {
            return false;
        }
        let dataType = getDataType(data);
        if (rootName === 'TEMPLATE') {
            host.innerHTML = data.toString();
        }
        else {
            if (dataType === 'Array' && data.length > 0) {
                for (let k of data) {
                    let childType = getDataType(k);
                    if (childType.includes('HTML')) {
                        host.appendChild(k);
                    }
                    else {
                        let child = createEl(k.name, k.attrs, k.content);
                        child && host.appendChild(child);
                    }
                }
            }
            else if (dataType.includes('HTML')) {
                host.appendChild(data);
            }
            else if (dataType === 'String' && data.trim().startsWith('#') && data.trim().length > 1) {
                let el = getEl(data);
                if (!el)
                    return;
                el.nodeName === 'TEMPLATE' ? host.appendChild(el.content.cloneNode(true)) : host.insertAdjacentHTML('beforeEnd', el.innerHTML);
            }
            else {
                host.insertAdjacentHTML('beforeEnd', data);
            }
        }
    };
    if (attrs && attrsType === 'Object') {
        for (let k in attrs) {
            attrs.hasOwnProperty(k) && rootEl.setAttribute(k, attrs[k]);
        }
    }
    loop(rootEl, content);
    return rootEl;
};

const trim = (str, placement) => {
    requireTypes(str, 'string');
    return placement === 'start' ? str.trimStart() :
        placement === 'end' ? str.trimEnd() :
            placement === 'both' ? str.trim() :
                placement === 'global' ? str.replace(/[\s\r\n]+/g, '') : str.trim().replace(/[\s\r\n]+/g, ' ');
};

const allToEls = (data, parent) => {
    if (isEmpty(data))
        return [];
    let result = [], type = getDataType(data);
    if (type.includes('HTML')) {
        result.push(data);
    }
    else if (type === 'String') {
        let str = trim(data), separator = str.includes(config.splitHyphen) ? config.splitHyphen : config.wordHyphen;
        str.split(separator).forEach(k => {
            let el = getEl(k, parent);
            el && result.push(el);
        });
    }
    else if (type === 'Array') {
        data.forEach(k => {
            let el = getEl(k, parent);
            el && result.push(el);
        });
    }
    else if (type === 'NodeList') {
        result = [...data];
    }
    return result;
};

const ajax = (options) => {
    if (isEmpty(options)) {
        throw new Error(`There is no options!`);
    }
    let dft = {
        target: '',
        url: '',
        type: 'post',
        async: true,
        data: null,
        holdTime: 0,
        stopTime: 3600000,
        contType: '',
        headers: {},
        respType: '',
        catchable: false,
        spinStr: `<ax-spin></ax-spin>`,
        spinSel: '',
        xhrName: '',
        repeat: {
            index: 0,
            max: 0,
            keyword: '',
        },
        xhrFields: {},
        abort: (resp) => { },
        timeout: (resp) => { },
        opened: (resp) => { },
        before: (resp) => { },
        downloading: (resp) => { },
        uploading: (resp) => { },
        complete: (resp) => { },
        success: (resp) => { },
        error: (resp) => { },
        cb: (resp) => { },
    };
    extend({ target: dft, source: options });
    !dft.type && (dft.type = 'post');
    let label = createEl('span', { [alias]: 'message' }), target = getEl(dft.target);
    target && (target.innerHTML = '', target.appendChild(label));
    let dftAbort = () => {
        target ? label.innerHTML = config.lang.ajax.abort : console.warn('The request has been suspended!');
    }, dftTimeout = () => {
        target ? label.innerHTML = config.lang.ajax.timeout : console.warn('The request is out of time!');
    }, dftBefore = (res) => {
        target && (label.innerHTML = res.content);
    }, dftSuccess = (res) => {
        target && (target.innerHTML = res.content);
    }, dftError = (res) => {
        target ? label.innerHTML = renderTpl(config.lang.ajax.error, { status: res.status }) : console.error(`The current error state is:${res.status}`);
    };
    let spinEls = allToEls(dft.spinSel), xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"), params;
    if (!isEmpty(dft.data)) {
        let dataType = getDataType(dft.data);
        if (dataType === 'FormData') {
            params = dft.data;
        }
        else if (dataType === 'Object') {
            if (dft.contType?.includes('json')) {
                params = JSON.stringify(dft.data);
            }
            else {
                
                params = new URLSearchParams(dft.data).toString();
                dft.contType = 'application/x-www-form-urlencoded';
            }
        }
        else {
            dft.data = dft.data.trim();
            params = dft.data.startsWith('&') || dft.data.startsWith('?') ? dft.data.slice(1) : dft.data;
            dft.contType = 'application/x-www-form-urlencoded';
        }
    }
    if (dft.contType) {
        dft.headers['Content-Type'] = dft.contType;
        if (!dft.contType.includes('urlencoded') && dft.type.toLowerCase() === 'get') {
            throw new Error(`Please request data with post!`);
        }
    }
    xhr.timeout = dft.stopTime;
    xhr.responseType = dft.respType;
    let dftResp = { target, label, xhr, spins: spinEls }, timeoutFun, errorFun, successFun, abortFun = () => {
        timeoutFun && xhr.removeEventListener('timeout', timeoutFun);
        xhr.abort();
        target && target.setAttribute(config.attrs.ajaxState, 'abort');
        !isEmpty(dft.abort) ? dft.abort(Object.assign({ status: xhr.status, content: xhr.response }, dftResp)) : dftAbort();
    };
    return new Promise((resolve, reject) => {
        let progress = (e, cb) => {
            let current = e.loaded, total = e.total, ratio = 0, percent = 0, result = '0%', time = (new Date(e.timeStamp)).getTime();
            if (e.lengthComputable) {
                ratio = current / total;
                percent = Math.round(ratio * 100);
                result = percent + '%';
                let data = Object.assign({ result, percent, current, total, ratio, time, status: xhr.status, abort: abortFun }, dftResp);
                cb && cb(data);
                percent >= 100 && dft.complete && dft.complete(data);
            }
        };
        xhr.upload.onprogress = (e) => {
            progress(e, (data) => {
                !isEmpty(dft.uploading) && dft.uploading(data);
            });
        };
        xhr.onprogress = (e) => {
            progress(e, (data) => {
                !isEmpty(dft.downloading) && dft.downloading(data);
            });
        };
        timeoutFun = () => {
            let resp = Object.assign({ status: xhr.status, content: xhr.response }, dftResp);
            target && target.setAttribute(config.attrs.ajaxState, 'timeout');
            !isEmpty(dft.timeout) ? dft.timeout(resp) : dftTimeout();
            dft.catchable && reject(resp);
            dft.cb && dft.cb(resp);
        };
        errorFun = (resp) => {
            target && target.setAttribute(config.attrs.ajaxState, 'error');
            !isEmpty(dft.error) ? dft.error(resp) : dftError(resp);
            dft.catchable && reject(resp);
            dft.cb && dft.cb(resp);
        };
        successFun = (resp) => {
            if ((dft.repeat.max && dft.repeat.keyword) && (ax.ajaxStorage[dft.repeat.index] < dft.repeat.max) && (typeof resp.content === 'string' && resp.content.includes(dft.repeat.keyword))) {
                ajax(dft);
                ax.ajaxStorage[dft.repeat.index]++;
            }
            else {
                ax.ajaxStorage[dft.repeat.index] = 0;
                if (spinEls.length > 0) {
                    for (let k of spinEls)
                        k.removeAttribute(config.attrs.ajaxSpin);
                }
                target && target.setAttribute(config.attrs.ajaxState, 'success');
                !isEmpty(dft.success) ? dft.success(resp) : dftSuccess(resp);
                resolve(resp);
                dft.cb && dft.cb(resp);
            }
        };
        xhr.addEventListener('timeout', timeoutFun);
        xhr.onreadystatechange = function () {
            if (xhr.readyState < 4) {
                let resp = Object.assign({ status: xhr.status, content: dft.spinStr, abort: abortFun }, dftResp);
                if (xhr.readyState === 1) {
                    dft.xhrName && (this[dft.xhrName] = xhr);
                    !isEmpty(dft.opened) && dft.opened(resp);
                }
                if (spinEls.length > 0) {
                    for (let k of spinEls)
                        k.toggleAttribute(config.attrs.ajaxSpin, true);
                }
                target && target.setAttribute(config.attrs.ajaxState, 'before');
                !isEmpty(dft.before) ? dft.before(resp) : dftBefore(resp);
            }
            else {
                let resp = Object.assign({ status: xhr.status }, dftResp);
                if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
                    if (!dft.respType || xhr.responseType === 'text') {
                        let div = createEl('div', '', xhr.responseText), content = '', reg = /(<head\b[^<]*(?:(?!<\/head>)<[^<]*)*<\/head>)|(<\/?html.*?>)|(<\!DOCTYPE.*?html.*?>)|(<\/?body.*?>)/gi, strEnd = sliceStrEnd({ str: dft.url }), childSel = (typeof dft.data === 'string' ? dft.data : strEnd), childDom = getEl(childSel, div);
                        if (childDom) {
                            content = childDom.innerHTML;
                        }
                        else if (!isEmpty(childSel) && !childDom) {
                            console.warn(`The node of "${childSel}" is not exist!`);
                            content = xhr.responseText.replace(reg, '').trim();
                        }
                        else {
                            if (xhr.responseText.trim().startsWith('{') || xhr.responseText.trim().startsWith('[')) {
                                content = JSON.parse(xhr.responseText.trim());
                            }
                            else {
                                content = xhr.responseText.replace(reg, '').trim();
                            }
                        }
                        resp.content = content;
                    }
                    else {
                        resp.content = xhr.response;
                    }
                    if (dft.holdTime) {
                        delay({
                            duration: dft.holdTime,
                            done: () => {
                                successFun(resp);
                            }
                        });
                    }
                    else {
                        successFun(resp);
                    }
                }
                else {
                    resp.content = xhr.response;
                    if (dft.holdTime) {
                        delay({
                            duration: dft.holdTime,
                            done: () => {
                                errorFun(resp);
                            }
                        });
                    }
                    else {
                        errorFun(resp);
                    }
                }
            }
        };
        let openParams = [], sendParams = null;
        if (dft.type.toLowerCase() === 'get') {
            openParams = ['get', dft.url + '?now=' + Date.now() + '&' + params, dft.async];
        }
        else if (dft.type.toLowerCase() === 'post') {
            openParams = ['post', dft.url, dft.async];
            sendParams = params;
        }
        for (let k in dft.xhrFields) {
            dft.xhrFields.hasOwnProperty(k) && (xhr[k] = dft.xhrFields[k]);
        }
        xhr.open(...openParams);
        for (let k in dft.headers) {
            dft.headers.hasOwnProperty(k) && xhr.setRequestHeader(k, dft.headers[k]);
        }
        xhr.send(sendParams);
    });
};

const valToArr = (data, hyphen, toNum = true) => {
    let dataType = getDataType(data), arr = [];
    if ([undefined, null, 'undefined', 'null', ''].includes(data)) {
        return arr;
    }
    if (dataType === 'Array') {
        arr = data.filter((k) => !isNull(k));
    }
    else if (dataType === 'String') {
        arr = data.trim().split(hyphen || config.splitHyphen);
        arr = arr.filter((k) => {
            k = k.trim();
            return k !== '' && !isNull(k);
        });
        toNum && (arr = arr.map((k) => isNaN(+k) ? k : +k));
    }
    else {
        arr.push(data);
    }
    return [...new Set(arr)];
};

const getValsFromAttrs = (el) => {
    let elem = getEl(el), obj = {};
    if (elem) {
        obj.label = elem.textContent.trim();
        let attrs = [...elem.attributes];
        attrs.forEach(k => {
            obj[k.name] = (['selected', 'disabled', 'readonly', 'active', 'checked', 'expanded', 'draggable'].includes(k.name)) ? true : k.value.trim();
        });
        !obj.hasOwnProperty('value') && (obj.value = obj.label);
        obj.value && !obj.label && (obj.label = obj.value);
    }
    return obj;
};

const select2Tree = (el) => {
    let elem = getEl(el), result = [];
    if (!elem) {
        console.warn(`No node, no access to data!`);
        return result;
    }
    let getChild = () => {
        let data = [];
        [...elem.children].forEach(k => {
            data.push(getJson(k));
        });
        return data;
    }, getJson = (elem) => {
        let obj = { node: elem, ...getValsFromAttrs(elem) };
        if (elem.nodeName == 'OPTGROUP') {
            obj.children = [];
            [...elem.children].forEach(k => {
                obj.children.push(getJson(k));
            });
        }
        return obj;
    };
    result = getChild();
    return result;
};

const ul2Tree = (el, type = 'tree') => {
    let elem = getEl(el), result = [];
    if (!elem) {
        console.warn(`No node, no access to data!`);
        return result;
    }
    let getChild = () => {
        let data = [];
        [...elem.children].forEach(k => {
            data.push(getJson(k));
        });
        return data;
    }, getJson = (elem) => {
        let obj = { node: elem.firstElementChild }, tmpLabel = obj.node.querySelector(`:scope> [${alias}="label"]`), tmpBadge = obj.node.querySelector(`:scope> [${alias}="badge"]`) || obj.node.querySelector(`:scope> AX-BADGE`), tmpTips = obj.node.querySelector(`:scope> [${alias}="tips"]`), tmpIcon = obj.node.querySelector(`:scope> [${alias}="icon"]`), tmpDisk = obj.node.querySelector(`:scope> [${alias}="disk"]`), tmpCube = obj.node.querySelector(`:scope> [${alias}="cube"]`), tmpCustom = obj.node.querySelector(`:scope> [${alias}="custom"]`), tmpBrief = elem.querySelector(`:scope> .${prefix}${type}-brief`), tmpCont = elem.querySelector(`:scope> .${prefix}${type}-cont`), tmpExtra = elem.querySelector(`:scope> .${prefix}${type}-extra`);
        obj.node.classList.contains(`${prefix}${type}-head`) && (obj.headEl = obj.node);
        tmpLabel && (obj.labelEl = tmpLabel);
        tmpBadge && (obj.badgeEl = tmpBadge);
        tmpTips && (obj.tipsEl = tmpTips);
        tmpCustom && (obj.customEl = tmpCustom);
        tmpIcon && (obj.iconEl = tmpIcon);
        tmpDisk && (obj.diskEl = tmpDisk);
        tmpCube && (obj.cubeEl = tmpCube);
        tmpBrief && (obj.briefEl = tmpBrief);
        tmpCont && (obj.contEl = tmpCont);
        tmpExtra && (obj.extraEl = tmpExtra);
        Object.assign(obj, { ...getValsFromAttrs(obj.wrapEl), ...getValsFromAttrs(obj.node), ...getValsFromAttrs(obj.labelEl) });
        obj.headEl && !obj.labelEl && (obj.headEl.innerHTML = '');
        let parent = elem.querySelector(':scope> ul,:scope> ol');
        if (parent) {
            obj.children = [];
            [...parent.children].forEach(k => {
                obj.children.push(getJson(k));
            });
        }
        return obj;
    };
    result = getChild();
    return result;
};

const getContent = async function (opts) {
    let dataType = getDataType(opts.content), getListArr = (data) => {
        let el = getEl(data, opts.parent) || createEl('div', {}, data), nodeName = el.nodeName, target, result;
        if (el) {
            if (['SELECT', 'DATALIST', 'UL', 'OL'].includes(nodeName)) {
                target = el;
            }
            else if (nodeName === 'TEMPLATE') {
                target = el.content.cloneNode(true).querySelector('ul,ol,select,datalist');
            }
            else {
                target = el.querySelector('ul,ol,select,datalist');
            }
        }
        if (target) {
            let tgtName = target.nodeName;
            result = (['SELECT', 'DATALIST',].includes(tgtName)) ? select2Tree(target) :
                (['UL', 'OL'].includes(tgtName)) ? ul2Tree(target) : [];
        }
        else {
            result = [];
        }
        return result;
    }, getIframe = (src) => {
        let dft = { src }, iframe = createEl('iframe', Object.assign(dft, opts.contData)), media = createEl('div', { [alias]: 'media' }, iframe);
        return createEl('div', { class: `${prefix}box-iframe` }, media);
    }, getImage = (src) => {
        let dft = { src }, image = createEl('img', Object.assign(dft, opts.contData)), media = createEl('div', { [alias]: 'media' }, image);
        return createEl('div', { class: `${prefix}box-image` }, media);
    }, getVideo = (src) => {
        let dft = { src, controls: 'controls' }, video = createEl('video', Object.assign(dft, opts.contData)), media = createEl('div', { [alias]: 'media' }, video);
        return createEl('div', { class: `${prefix}box-video` }, media);
    }, getAudio = (src) => {
        let dft = { src, controls: 'controls' }, audio = createEl('audio', Object.assign(dft, opts.contData)), media = createEl('div', { [alias]: 'media' }, audio);
        return createEl('div', { class: `${prefix}box-audio` }, media);
    }, getMedias = (type) => {
        let map = {
            iframe: getIframe,
            audio: getAudio,
            video: getVideo,
            image: getImage,
        }, vals = valToArr(opts.content, opts.hyphen || config.splitHyphen).map((k) => k.trim()), arr = [];
        for (let k of vals) {
            arr.push(map[type](k));
        }
        return arr;
    }, getAsync = async (url) => {
        let data, options = Object.assign({
            url,
            data: opts.contData,
            success: (resp) => {
                data = resp.content;
            },
            cb: (resp) => {
                opts.request && opts.request(resp);
            }
        }, opts.ajax);
        await ajax.call(this, options);
        return data;
    }, getAsyncs = async () => {
        let vals = valToArr(opts.content, opts.hyphen || config.splitHyphen), arr = [];
        for (let k of vals) {
            await getAsync(k).then((resp) => {
                arr.push(resp);
            });
        }
        return arr;
    }, result;
    if (dataType === 'Number') {
        result = opts.content + '';
    }
    else if (dataType === 'String') {
        opts.content = opts.content.trim();
        if (opts.contType === 'text') {
            result = opts.content;
        }
        else if (opts.contType === 'html') {
            result = getEl(opts.content, opts.parent) ? getEl(opts.content, opts.parent).innerHTML : '';
        }
        else if (opts.contType === 'node') {
            result = getEl(opts.content, opts.parent);
        }
        else if (opts.contType === 'list') {
            result = getListArr(opts.content);
        }
        else if (opts.contType === 'form') {
            let tmp = getEl(opts.content, opts.parent), elem = tmp?.nodeName === 'TEMPLATE' ? tmp.content.cloneNode(true) : tmp;
            result = elem?.querySelector('form') || '';
        }
        else if (opts.contType === 'iframe') {
            result = getIframe(opts.content);
        }
        else if (opts.contType === 'iframes') {
            result = getMedias('iframe');
        }
        else if (opts.contType === 'image') {
            result = getImage(opts.content);
        }
        else if (opts.contType === 'images') {
            result = getMedias('image');
        }
        else if (opts.contType === 'video') {
            result = getVideo(opts.content);
        }
        else if (opts.contType === 'videos') {
            result = getMedias('video');
        }
        else if (opts.contType === 'audio') {
            result = getAudio(opts.content);
        }
        else if (opts.contType === 'audios') {
            result = getMedias('audio');
        }
        else if (opts.contType === 'async') {
            await getAsync(opts.content).then((resp) => {
                result = resp;
            });
        }
        else if (opts.contType === 'asyncs') {
            await getAsyncs().then((resp) => {
                result = resp;
            });
        }
        else if (fieldTypes.includes(opts.contType)) {
            let attrs = opts.contType === 'datetime' ? { feature: 'flat', full: true } : opts.contType === 'upload' ? { 'feature': 'gallery' } : ['radios', 'checkboxes', 'checkbox'].includes(opts.contType) ? { type: 'text' } : { full: true }, valProp = ['checkboxes', 'radios', 'select'].includes(opts.contType) ? 'content' :
                ['checkbox', 'radio'].includes(opts.contType) ? 'label' :
                    opts.contType === 'upload' ? 'url' :
                        opts.contType === 'file' ? 'placeholder' : 'value', dft = { [valProp]: opts.content, ...attrs, ...Object.assign({ name: ax.namePfx + Date.now() }, opts.contData) };
            result = createEl(`ax-${opts.contType}`, dft);
        }
        else {
            result = opts.content;
        }
    }
    else if (dataType === 'Promise') {
        await opts.content.then((resp) => {
            result = resp;
        });
    }
    else if (dataType === 'AsyncFunction') {
        await opts.content.call(this).then((resp) => {
            result = resp;
        });
    }
    else if (dataType === 'Function') {
        result = opts.content.call(this);
    }
    else if (dataType === 'Object' || dataType === 'Array') {
        if (dataType === 'Object' && opts.content[config.reqProp]) {
            await getContent(opts.content).then((resp) => {
                result = resp;
            });
        }
        else {
            result = deepClone(opts.content);
        }
    }
    else if (dataType.includes('HTML') && opts.contType === 'list') {
        result = getListArr(opts.content);
    }
    else {
        result = opts.content;
    }
    opts.cb && opts.cb.call(this, result);
    return result;
};

const style = (elem, pseudo) => {
    let el = getEl(elem), result = el ? document.defaultView.getComputedStyle(el, pseudo) : {};
    return result;
};

const getClasses = (data) => {
    let dataType = getDataType(data), separator, result = [];
    if (dataType === 'Array') {
        result = data.filter((k) => k && typeof k === 'string');
    }
    else {
        data = trim(data);
        separator = data.includes(config.splitHyphen) ? config.splitHyphen : config.wordHyphen;
        result = data.split(separator);
    }
    return result.map((k) => trim(k, 'global')).filter(Boolean);
};

const classes = (target) => {
    let el = getEl(target);
    return {
        
        get: () => el ? getClasses(el.getAttrubute('class')) : [],
        
        add: function (classes, intercept) {
            let arr = getClasses(classes);
            if (!el || arr.length === 0) {
                return this;
            }
            arr.forEach((k) => {
                let tmp;
                if (intercept) {
                    tmp = intercept(k);
                    tmp === true ? el.classList.add(k) :
                        (typeof tmp === 'string' && tmp) ? el.classList.add(tmp) : null;
                }
                else {
                    el.classList.add(k);
                }
            });
            return this;
        },
        
        remove: function (classes, intercept) {
            let arr = getClasses(classes);
            if (!el || arr.length === 0)
                return this;
            arr.forEach((k) => {
                let tmp;
                if (intercept) {
                    tmp = intercept(k);
                    tmp === true ? el.classList.remove(k) :
                        (typeof tmp === 'string' && tmp) ? el.classList.remove(tmp) : null;
                }
                else {
                    el.classList.remove(k);
                }
            });
            return this;
        },
        
        replace: function (oldClass, newClass) {
            if (!el || !oldClass)
                return this;
            if (el && newClass && oldClass) {
                el.classList.remove(oldClass);
                el.classList.add(newClass);
            }
            return this;
        },
        
        has: function (classes) {
            if (!el || isEmpty(classes))
                return this;
            let arr = getClasses(classes);
            for (let k of arr) {
                if (!el.classList.contains(k)) {
                    return false;
                }
            }
            return true;
        }
    };
};

const elState = (elem) => {
    let dom = getEl(elem), isExist = dom ? true : false, result;
    if (!dom) {
        result = { isExist, isCalc: false, isUncalc: true };
    }
    else {
        let style = getComputedStyle(dom), isVirtual = !style.display, isHidden = (style.display === 'none' || style.visibility === 'hidden'), isCalc = !!(style.display && style.display !== 'none'), isVisible = !!(isCalc && style.visibility === 'visible');
        result = { isExist, isVirtual, isHidden, isVisible, isCalc, isUncalc: isVirtual || isHidden };
    }
    return result;
};

const optBase = [
    {
        attr: 'ins-name',
        prop: 'insName',
        value: '',
    },
    {
        attr: 'stor-name',
        prop: 'storName',
        value: '',
    },
    {
        attr: 'stor-keys',
        prop: 'storKeys',
        value: [],
    },
    {
        attr: 'breakpoints',
        prop: 'breakpoints',
        value: {},
    },
    {
        attr: 'lang',
        prop: 'lang',
        value: {},
    },
    {
        attr: 'b4-init',
        prop: 'b4Init',
        value: null,
    },
    {
        attr: 'on-constructed',
        prop: 'onConstructed',
        value: null,
    },
    {
        attr: 'on-initiate',
        prop: 'onInitiate',
        value: null,
    },
    {
        attr: 'on-initiated',
        prop: 'onInitiated',
        value: null,
    },
    {
        attr: 'on-destroyed',
        prop: 'onDestroyed',
        value: null,
    },
    {
        attr: 'on-error',
        prop: 'onError',
        value: null,
    },
    {
        attr: 'on-reset',
        prop: 'onReset',
        value: null,
    },
    {
        attr: 'on-updated',
        prop: 'onUpdated',
        value: null,
    },
    {
        attr: 'on-saved',
        prop: 'onSaved',
        value: null,
    },
    {
        attr: 'on-clearedcache',
        prop: 'onClearedCache',
        value: null,
    },
    {
        attr: 'on-updatedcache',
        prop: 'onUpdatedCache',
        value: null,
    },
];

let AXTMP_actClass = config.actClass;
const optBubble = [
    {
        attr: 'heading',
        prop: 'heading',
        value: '',
    },
    {
        attr: 'divider',
        prop: 'divider',
        value: false,
    },
    {
        attr: 'duration',
        prop: 'duration',
        value: 0,
    },
    {
        attr: 'auto-dur',
        prop: 'autoDur',
        value: false,
    },
    {
        attr: 'asleep',
        prop: 'asleep',
        value: false,
    },
    {
        attr: 'dead-show',
        prop: 'deadShow',
        value: false,
    },
    {
        attr: 'keep-show',
        prop: 'keepShow',
        value: false,
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'padding',
        prop: 'padding',
        value: {
            enable: true,
            value: '',
        }
    },
    {
        attr: 'content',
        prop: 'content',
        value: '',
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'act-class',
        prop: 'actClass',
        value: AXTMP_actClass,
    },
    {
        attr: 'tpl-str',
        prop: 'tplStr',
        value: '',
    },
    {
        attr: 'tpl-eng',
        prop: 'tplEng',
        value: null,
    },
    {
        attr: 'z-index',
        prop: 'zIndex',
        value: 0,
    },
    {
        attr: 'dedicated',
        prop: 'dedicated',
        value: false,
    },
    {
        attr: 'can-click',
        prop: 'canClick',
        value: null,
    },
    {
        attr: 'media',
        prop: 'media',
        value: {
            caption: '',
            brief: '',
        },
    },
    {
        attr: 'auto-fill',
        prop: 'autoFill',
        value: {
            enable: false,
            inputSel: '',
            childSel: '',
            detectable: false,
            attr: '',
        },
    },
    {
        attr: 'bullet',
        prop: 'bullet',
        value: {
            enable: false,
            type: 'custom',
            parentNode: 'ul',
            childNode: 'li',
            unpadded: false,
            hoverable: false,
            divisible: false,
            multiline: false,
            cols: 0,
            lines: '',
            action: null,
        },
    },
    {
        attr: 'tools',
        prop: 'tools',
        value: {
            enable: true,
            placement: 'inside',
            children: ['close']
        },
        
    },
    {
        attr: 'footer',
        prop: 'footer',
        value: {
            enable: true,
            layout: 'plain',
            note: false,
            divider: false,
            padding: false,
            children: ['cancel', 'confirm']
        }
        
    },
    {
        attr: 'b4-show',
        prop: 'b4Show',
        value: null,
    },
    {
        attr: 'b4-hide',
        prop: 'b4Hide',
        value: null,
    },
    {
        attr: 'b4-fill',
        prop: 'b4Fill',
        value: null,
    },
    {
        attr: 'b4-confirm',
        prop: 'b4Confirm',
        value: null,
    },
    {
        attr: 'b4-cancel',
        prop: 'b4Cancel',
        value: null,
    },
    {
        attr: 'on-show',
        prop: 'onShow',
        value: null,
    },
    {
        attr: 'on-hide',
        prop: 'onHide',
        value: null,
    },
    {
        attr: 'on-shown',
        prop: 'onShown',
        value: null,
    },
    {
        attr: 'on-hidden',
        prop: 'onHidden',
        value: null,
    },
    {
        attr: 'on-invalidated',
        prop: 'onInvalidated',
        value: null,
    },
    {
        attr: 'on-validated',
        prop: 'onValidated',
        value: null,
    },
    {
        attr: 'on-sended',
        prop: 'onSended',
        value: null,
    },
    {
        attr: 'on-render',
        prop: 'onRender',
        value: null,
    },
    {
        attr: 'on-rendered',
        prop: 'onRendered',
        value: null,
    },
    {
        attr: 'on-updatedcont',
        prop: 'onUpdatedCont',
        value: null,
    },
    {
        attr: 'on-transferred',
        prop: 'onTransferred',
        value: null,
    },
    {
        attr: 'on-confirmed',
        prop: 'onConfirmed',
        value: null,
    },
    {
        attr: 'on-canceled',
        prop: 'onCanceled',
        value: null,
    }, {
        attr: 'on-closed',
        prop: 'onClosed',
        value: null,
    }, {
        attr: 'on-cleared',
        prop: 'onCleared',
        value: null,
    },
    {
        attr: 'on-request',
        prop: 'onRequest',
        value: null
    },
    {
        attr: 'on-bulletschecked',
        prop: 'onBulletsChecked',
        value: null
    },
    {
        attr: 'on-bulletsselected',
        prop: 'onBulletsSelected',
        value: null
    },
    {
        attr: 'on-targetset',
        prop: 'onTargetSet',
        value: null
    },
    ...optBase
];

const optDrawer = [
    {
        attr: 'placement',
        prop: 'placement',
        value: 'right',
    },
    {
        attr: 'trigger',
        prop: 'trigger',
        value: 'click',
    },
    {
        attr: 'offset',
        prop: 'offset',
        value: '100px',
    },
    {
        attr: 'size',
        prop: 'size',
        value: 'sm',
    },
    {
        attr: 'parent',
        prop: 'parent',
        value: '',
    },
    {
        attr: 'wing',
        prop: 'wing',
        value: {
            selector: '',
            actClass: '',
        },
    },
    {
        attr: 'mask',
        prop: 'mask',
        value: {
            enable: true,
            closable: true,
        },
    },
    ...optBubble
];

const getEvtTarget = (evt) => {
    
    if (!evt.targetTouches) {
        return evt.target;
    }
    else {
        let tmp = evt.targetTouches[0] || evt.changedTouches[0];
        return document.elementFromPoint(tmp.clientX, tmp.clientY);
    }
};

const removeStyle = (el, prop) => {
    let target = getEl(el);
    if (!target || !prop)
        return;
    target.style.cssText = target.style.cssText.replace(`${prop}:`, '');
};

const addStyle = (el, key, value) => {
    let target = getEl(el);
    key = (key + '').toLowerCase();
    value = value + '';
    if (!target || !key || !value)
        return;
    let prop = key.replace(/-([a-z])/g, (match, k) => k.toUpperCase());
    target.style[prop] = value;
};

const isDateStr = (str) => (typeof str === 'string' && !isNaN(Date.parse(str)));

const getUTCTimestamp = (value) => {
    let date, offset, dateTamp, valType = getDataType(value);
    if (valType === 'Date') {
        date = value;
    }
    else if (valType === 'String') {
        if (isDateStr(value)) {
            date = new Date(value);
        }
        else {
            date = new Date();
        }
    }
    else if (valType === 'Number') {
        date = new Date(value);
    }
    else {
        date = new Date();
    }
    dateTamp = date.getTime();
    offset = date.getTimezoneOffset() * 60000;
    return dateTamp + offset;
};

const getExpiration = (type = 'd', value = 365, output = 'locale') => {
    let dateExp;
    if (isDateStr(value)) {
        dateExp = new Date(value);
    }
    else {
        dateExp = new Date();
        switch (type) {
            case 's':
                dateExp.setSeconds(dateExp.getSeconds() + ~~value);
                break;
            case 'm':
                dateExp.setMinutes(dateExp.getMinutes() + ~~value);
                break;
            case 'h':
                dateExp.setHours(dateExp.getHours() + ~~value);
                break;
            default:
                dateExp.setDate(dateExp.getDate() + ~~value);
        }
    }
    return output === 'utc' ? getUTCTimestamp(dateExp) : dateExp.getTime();
};

const storage = {
    
    set: function (key, value, opts) {
        if (isEmpty(key) && typeof key !== 'string')
            return;
        let options = Object.assign({ unit: 'd', expires: 0, override: true, type: 'local' }, opts), newValue = deepClone(value), valType = getDataType(value), stor = options.type === 'session' ? window.sessionStorage : window.localStorage, filterProps = (obj) => {
            Object.keys(obj).forEach(k => {
                let val = obj[k], type = getDataType(val);
                ['Array', 'Object'].includes(type) && filterProps(val);
                (type.includes('HTML') || type.includes('Function') || ['Promise', 'Symbol', 'Instance'].includes(type)) && delete obj[k];
            });
            return obj;
        };
        if (['Array', 'Object'].includes(valType) && !isEmpty(newValue)) {
            filterProps(newValue);
        }
        let expires = options.expires !== 0 ? getExpiration(options.unit, options.expires) : 0, oldValue = this.get(key, options.type), valueExp = {
            data: (!options.override && this.get(key, options.type)) ? extend({ target: oldValue, source: newValue }) : newValue,
            expires,
        };
        if (((typeof options.expires === 'number' && options.expires < 0) || newValue === null) && this.get(key, options.type)) {
            this.remove(key, options.type);
        }
        else {
            stor.setItem(key, JSON.stringify(valueExp));
        }
        return this;
    },
    
    get: function (key, type = 'local') {
        if (isEmpty(key) || typeof key !== 'string') {
            return false;
        }
        let now = Date.now(), stor = type === 'session' ? window.sessionStorage : window.localStorage, valueStr = stor.getItem(key);
        if (valueStr) {
            let valueExp = (new Function('"use strict";return ' + valueStr))();
            if (valueExp.expires === 0 || now <= valueExp.expires) {
                return valueExp.data;
            }
            else {
                stor.removeItem(key);
                return null;
            }
        }
        else {
            return null;
        }
    },
    
    remove: function (key, type = 'local') {
        if (isEmpty(key) || typeof key !== 'string') {
            return false;
        }
        let stor = type === 'session' ? window.sessionStorage : window.localStorage;
        stor.removeItem(key);
        return this;
    },
    
    clear: function (type = 'local') {
        let stor = type === 'session' ? window.sessionStorage : window.localStorage;
        stor.clear();
        return this;
    }
};

const optObserve = [
    {
        attr: 'deep',
        prop: 'deep',
        value: {
            enable: false,
            include: [],
            exclude: [],
        },
    },
    {
        attr: 'filter',
        prop: 'filter',
        value: null,
    },
    {
        attr: 'tpyes',
        prop: 'tpyes',
        value: ['Object', 'Array', 'Function'],
    },
    {
        attr: 'accept',
        prop: 'accept',
        value: true,
    },
    {
        attr: 'lenient',
        prop: 'lenient',
        value: true,
    },
    {
        attr: 'once',
        prop: 'once',
        value: false,
    },
    {
        attr: 'methods',
        prop: 'methods',
        value: ['set', 'delete'],
    },
    {
        attr: 'on-added',
        prop: 'onAdded',
        value: null,
    },
    {
        attr: 'on-edited',
        prop: 'onEdited',
        value: null,
    },
    {
        attr: 'on-set',
        prop: 'onSet',
        value: null,
    },
    {
        attr: 'on-deleted',
        prop: 'onDeleted',
        value: null,
    },
    {
        attr: 'on-crud',
        prop: 'onCrud',
        value: null,
    },
    {
        attr: 'on-new',
        prop: 'onNew',
        value: null,
    },
    {
        attr: 'on-applied',
        prop: 'onApplied',
        value: null,
    },
    {
        attr: 'on-trigger',
        prop: 'onTrigger',
        value: null,
    },
    {
        attr: 'on-completed',
        prop: 'onCompleted',
        value: null,
    },
    ...optBase
];

const deepEqual = (a, b) => {
    if (a === b)
        return true;
    let typeA = getDataType(a), typeB = getDataType(b);
    if (typeA === typeB && typeA === 'Object') {
        let getStr = (data) => {
            let cache = new Set();
            return JSON.stringify(data, (key, val) => {
                if (typeof val === 'object' && val !== null) {
                    if (cache.has(val))
                        return;
                    cache.add(val);
                }
                return val;
            });
        };
        return getStr(a) === getStr(b);
    }
    else {
        return false;
    }
};

const isProxy = (obj) => obj?._isProxy === true;

const unique = (data, prop) => {
    let result;
    if (!isEmpty(data)) {
        result = [...new Set(data)];
        if (typeof result[0] === 'object' && prop) {
            let map = new Map();
            for (let k of result) {
                !map.has(k[prop]) && map.set(k[prop], k);
            }
            result = [...map.values()];
        }
    }
    else {
        result = [...data];
    }
    return result;
};

const plan = {
    handle: (name, type, cb) => {
        let nameArr = [];
        if (type === 'String') {
            name = trim(name);
            nameArr = name.includes(',') ? name.split(',') : name.split(' ');
        }
        else if (type === 'Array') {
            nameArr = name.map((k) => trim(k));
        }
        if (nameArr.length > 0) {
            for (let k of nameArr) {
                cb(k);
            }
        }
    },
    
    add: function (name, instance, content) {
        let nameType = getDataType(name), handle = (str) => {
            if (!instance.plans.hasOwnProperty(str)) {
                instance.plans[str] = [];
            }
            let newValue = content.toString().replace(/\s/g, '').replace(/\;/g, '').match(/{(\S*)}/), oldValue = instance.plans[str].toString().replace(/\s/g, '').replace(/\;/g, '');
            newValue && !oldValue.includes(newValue[1]) ? instance.plans[str].push(content) : null;
        };
        this.handle(name, nameType, (item) => handle(item));
        return this;
    },
    
    remove: function (name, instance, content) {
        let nameType = getDataType(name), handle = (str) => {
            if (!instance.plans.hasOwnProperty(str)) {
                return console.warn(`The ${str} event is invalid or unusable!`);
            }
            if (!content) {
                delete instance.plans[str];
            }
            else {
                let index = instance.plans[str].findIndex((k) => k === content);
                if (index < 0) {
                    return console.warn(`The ${str} event is not bound!`);
                }
                instance.plans[str].splice(index, 1);
                instance.plans[str].length === 0 ? delete instance.plans[str] : null;
            }
        };
        this.handle(name, nameType, (item) => handle(item));
        return this;
    },
    
    do: function (name, instance, ...params) {
        let nameType = getDataType(name), handle = (str) => {
            if (!instance.plans.hasOwnProperty(str)) {
                return console.warn(`The ${str} event is unregistered or unusable!`);
            }
            instance.plans[str].forEach((k) => {
                k.call(instance, ...params);
            });
        };
        this.handle(name, nameType, (item) => handle(item));
        return this;
    }
};

const attrJoinVal = (attr, value, map) => {
    let item = map ? map.find((k) => k.attr === attr) : null, isFn = () => {
        let tmp = attr.trim(), condition = item ? typeof item?.value === 'function' : false;
        return (tmp.startsWith('on-') || tmp.startsWith('b4-')) || condition;
    }, addFnShell = (str) => {
        let tmp = str.trim(), result = (!(tmp.startsWith('function') || tmp.startsWith('(')) && !tmp.endsWith('}')) ? `function(){${str}}` : str;
        return result;
    }, fnVal = isFn() ? addFnShell(value) : value;
    try {
        if (item) {
            if (value === null) {
                return { [item.prop]: item.value };
            }
            else {
                let valType = getDataType(item.value), trim = value.trim(), trueVals = ['', '1', 'true', true], falseVals = [null, 'null', 'undefined', '0', 'false', 'NaN'];
                return item.type === 'ignore' ? { [item.prop]: value } :
                    (valType === 'String') ? { [item.prop]: value } :
                        (valType === 'Number') ? { [item.prop]: parseFloat(value) } :
                            (valType === 'Boolean' && trueVals.includes(trim)) ? { [item.prop]: true } :
                                (valType === 'Boolean' && falseVals.includes(trim)) ? { [item.prop]: false } :
                                    (valType === 'Object' && item.value.hasOwnProperty('enable') && trueVals.includes(trim)) ? { [item.prop]: true } :
                                        (valType === 'Object' && item.value.hasOwnProperty('enable') && item.value.hasOwnProperty('children') && trim.startsWith('[') && trim.endsWith(']')) ? { [item.prop]: { enable: true, children: parseStr({ content: `${value}`, type: 'array', catchable: true }) } } :
                                            (valType === 'Object' && value.includes(':')) ? { [item.prop]: strToJson(value) } :
                                                parseStr({ content: `{"${item.prop}":${fnVal}}`, type: 'object', catchable: true });
            }
        }
        else {
            return value === '' ? { [attr]: '' } : parseStr({ content: `{"${attr}":${fnVal}}`, type: 'object', catchable: true });
        }
    }
    catch (e) {
        return { [attr]: value };
    }
};

const breakpoints = (obj, points) => {
    if (isEmpty(obj)) {
        return false;
    }
    let valids = [], assign = {}, width = document.body.clientWidth, screenSize = getScreenSize(), validFun = (key, value) => {
        let _key = ~~key;
        if (_key === 0) {
            if (key.startsWith('screen')) {
                screenSize === key.split('-')[1] ? valids.push(value) : null;
            }
            else if (key.startsWith('width')) {
                ~~key.split('-')[1] > width ? valids.push(value) : null;
            }
            else if (key.startsWith('mobile')) {
                JSON.parse(key.split('-')[1]) === isMobi ? valids.push(value) : null;
            }
        }
        else {
            _key > width ? valids.push(value) : null;
        }
    };
    for (let k in points) {
        if (points.hasOwnProperty(k)) {
            Reflect.deleteProperty(points[k], 'breakpoints');
            validFun(k, points[k]);
        }
    }
    if (valids.length === 0) {
        return false;
    }
    assign = Object.assign({}, ...valids);
    extend({ target: obj, source: assign });
};

const elProps = (el) => {
    let target = getEl(el), result = null;
    if (target) {
        !target.hasOwnProperty('ax') ? target.ax = {} : null;
        result = {
            add: (key, value = true) => {
                key && (target.ax[key] = value);
                return target;
            },
            remove: (key) => {
                key && (delete target.ax[key]);
                return target;
            }
        };
    }
    return result;
};

const keyCond = (key) => {
    return !/^(stor-keys|ins-name|stor-name|storKeys|insName|storName|on-|b4-|on[A-Z]|b4[A-Z])/.test(key);
};

const spreadBool = (host, params) => {
    let result = host, hostType = getDataType(host), paramsType = getDataType(params);
    if (paramsType === 'Object') {
        if (hostType === 'Boolean') {
            Reflect.deleteProperty(params, 'enable');
            result = { enable: host, ...params };
        }
        else if (hostType === 'Object') {
            if (host.hasOwnProperty('enable')) {
                host = Object.assign({}, params, host);
            }
            else {
                for (let k in params) {
                    if (params.hasOwnProperty(k) && host.hasOwnProperty(k)) {
                        host[k] = spreadBool(host[k], params[k]);
                    }
                }
            }
        }
    }
    return result;
};

class ModBase {
    targetEl;
    targetData;
    options;
    destroyed;
    timestamp;
    renderCount;
    respSource;
    initialized;
    moduleName;
    dftOpts;
    rawOpts;
    rawHtml;
    rawEl;
    tplStr;
    tplEng;
    initCount;
    constructor() {
        this.timestamp = Date.now();
        this.respSource = null;
        this.initialized = false;
        this.initCount = 0;
        this.renderCount = 0;
        
        this.targetEl = null;
        
        this.targetData = null;
        
        this.rawOpts = {};
        this.dftOpts = {};
        this.options = {};
        this.rawHtml = '';
        this.rawEl = null;
        
        this.destroyed = false;
        this.moduleName = this.constructor.name.toLowerCase();
        this.tplStr = '';
        this.tplEng = null;
    }
    
    getOptsFromMap(map) {
        let options = {};
        for (let k of map) {
            options[k.prop] = k.value;
        }
        return options;
    }
    
    isComp(elem) {
        return elem.getAttribute(alias) === 'slot-host';
    }
    
    getElOptsMap(elem) {
        if (isEmpty(elem)) {
            return { el: null, attr: {} };
        }
        else {
            let isComp = this.isComp(elem), el = isComp ? elem.parentNode : elem, attr = isComp ? 'initiated' : `ax-${this.moduleName}`;
            return { el, attr };
        }
    }
    
    mergeOpts({ source = {}, map = [], el = null, component = false }) {
        let target = getEl(el), elMap = this.getElOptsMap(target), options = deepClone(this.getOptsFromMap(map));
        this.rawOpts = deepClone(options);
        !isEmpty(config.lang[this.moduleName]) && (options.lang = extend({ target: deepClone(config.lang[this.moduleName]), source: options.lang }));
        extend({ target: options, source: config[this.moduleName] || {} });
        extend({ target: options, source, host: elMap.el, attr: elMap.attr });
        component ? extend({ target: options, source: this.mergeCompAttrs(target, map) }) : null;
        this.dftOpts = deepClone(options);
        return options;
    }
    
    mergeCompAttrs(elem, map) {
        let options = {};
        if (this.isComp(elem)) {
            let target = elem.parentNode, attributes = target?.attributes;
            if (!target || target.nodeName.toLowerCase() !== `ax-${this.moduleName}` || !attributes)
                return {};
            for (let k of attributes) {
                let item = map.find((i) => i.attr === k.name);
                if (item) {
                    
                    let keyVal = attrJoinVal(item.attr, k.value, [item]);
                    Object.assign(options, keyVal);
                }
            }
        }
        return options;
    }
    
    getOptsAttr(elem) {
        return elem.nodeName === `AX-${this.moduleName.toUpperCase()}` ? 'options' : `ax-${this.moduleName}`;
    }
    
    getStrFromContent(str) {
        let el = getEl(str);
        return el ? el.innerHTML : str;
    }
    
    revertHtml(elem) {
        this.rawHtml ? elem.innerHTML = this.rawHtml : '';
        elem.children.length === 1 && (this.rawEl = elem.children[0]);
    }
    replaceMult(str = '', arr, opts = {}) {
        let options = Object.assign({ ignore: true, nodename: 'i' }, opts), regx = new RegExp(`(${arr.join('|')})`, `g${options.ignore ? 'i' : ''}`), tmp = `<${options.nodename} ${alias}="mark"${options.classes ? ' class="' + options.classes + '"' : ''}>$1</${options.nodename}>`;
        return (str + '').replace(regx, tmp);
        
    }
    parseLayout(host = this.targetEl, template = '', map = {}) {
        let target = getEl(host), holder = createEl('i', { [alias]: 'holder' }), gap = createEl('i', { [alias]: 'gap' }), condition = (key) => key === 'holder' ? holder.cloneNode(true) : key === 'gap' ? gap.cloneNode(true) : map[key], getNodes = (str) => str.split('|').map(condition).filter(Boolean);
        if (!target || !template || isEmpty(map))
            return;
        if (template.includes('(') && template.includes(')')) {
            let startIdx = template.indexOf('('), endIdx = template.indexOf(')'), strL = template.slice(0, startIdx), strC = template.slice(startIdx + 1, endIdx), strR = template.slice(endIdx + 1), nodesL = getNodes(strL), nodesC = getNodes(strC), nodesR = getNodes(strR);
            nodesL.length && target.append(...nodesL);
            if (map.group) {
                target.appendChild(map.group);
                nodesC.length && map.group.append(...nodesC);
            }
            nodesR.length && target.append(...nodesR);
        }
        else {
            let nodes = getNodes(template);
            
            target.append(...nodes);
        }
    }
    
    updateOpts(spread) {
        this.optsMergeStorage && this.optsMergeStorage();
        !isEmpty(this.options.breakpoints) ? breakpoints(this.options, this.options.breakpoints) : null;
        if (Array.isArray(spread) && spread.length > 0) {
            let obj = {};
            for (let k of spread) {
                obj[k] = this.rawOpts[k];
            }
            spreadBool(this.options, obj);
        }
    }
    
    getStorageKeys() {
        let result = [];
        if (Array.isArray(this.options.storKeys) && this.options.storKeys.length > 0) {
            result = this.options.storKeys;
        }
        else {
            result = Object.keys(this.options).filter((k) => keyCond(k));
        }
        return result;
    }
    
    single2Els(data, parent) {
        let result = [];
        if (data) {
            let dataType = getDataType(data);
            if (dataType === 'Array') {
                result = data;
            }
            else if (dataType.includes('HTML')) {
                result.push(data);
            }
            else {
                result = getEls(data, parent);
            }
        }
        return result.filter(Boolean);
    }
    
    useTpl() {
        let tplEl = getEl(this.options.tplStr);
        this.tplEng = this.options.tplEng || renderTpl;
        this.tplStr = tplEl?.innerHTML || this.options.tplStr;
    }
    getTplcont(data, str = this.tplStr, eng = this.tplEng) {
        let tmp = eng || renderTpl;
        return tmp.name === 'template' ? tmp(this.tplStr)(data) : tmp(str, data);
    }
    
    
    
    ready({ type = 'node', options = {}, maps = [], host, component = false, spread = [], }) {
        getDataType(host); let moreParams = {};
        this.propsMap = maps;
        if (type === 'node') {
            this.targetEl = getEl(host);
            if (this.targetEl) {
                elProps(this.targetEl)?.add(this.moduleName);
                moreParams = {
                    el: this.targetEl,
                    component,
                };
                this.rawHtml = this.targetEl.innerHTML;
                this.targetEl.children.length === 1 && (this.rawEl = this.targetEl.children[0]);
                this.compAttrs = [];
            }
        }
        else {
            type === 'data' && (this.targetData = host);
        }
        this.options = this.mergeOpts({
            source: options,
            map: this.propsMap,
            ...moreParams,
        });
        this.storKeys = this.getStorageKeys();
        this.createProxy && this.createProxy();
        this.plans = {};
        instance.push(this, this.options.insName, this.moduleName);
        this.on('initiate', () => {
            this.destroyed = false;
            this.updateOpts(spread);
            this.targetEl && this.targetEl.classList.add(`${prefix}initiated`);
            this.options.hasOwnProperty('tplStr') && this.useTpl();
        });
        this.on('initiated', () => {
            this.initialized = true;
            this.initCount++;
        });
        this.on('destroyed', () => {
            this.clearCache && this.clearCache();
            this.destroyed = true;
        });
    }
    moreExceed({ data, source, sliced = true, min = this.options.min, max = this.options.max }) {
        return new Promise((resolve, reject) => {
            let curLen = source.length, newLen = data.length, totalLen = curLen + newLen, param = { min, max, cur: curLen, val: newLen };
            if (max && totalLen > max) {
                this.listen({ name: 'tooMany', params: [param] });
                let tmp = max - curLen;
                if (!tmp) {
                    reject();
                    console.warn(`The maximum quantity (${max}) has been reached, no more new data can be added!`);
                }
                else {
                    if (sliced) {
                        resolve(data.slice(0, tmp));
                        console.warn('The amount of newly added data is too much, only part of it has been added!');
                    }
                    else {
                        reject();
                        console.warn('The amount of newly added data is too much, further additions have been prevented!');
                    }
                }
            }
            else if (totalLen < min) {
                console.warn('The amount of newly added data is too little, please continue adding!');
                this.listen({ name: 'tooFew', params: [param] });
                resolve(null);
            }
            else {
                resolve(null);
            }
        });
    }
    lessExceed({ data, source, sliced = true, min = this.options.min, max = this.options.max }) {
        return new Promise((resolve, reject) => {
            let curLen = source.length, newLen = data.length, totalLen = curLen - newLen, param = { min, max, cur: curLen, val: newLen };
            if (max && totalLen > max) {
                console.warn('The current data is still too much, please continue deleting!');
                this.listen({ name: 'tooMany', params: [param] });
                resolve(null);
            }
            else if (totalLen < min) {
                this.listen({ name: 'tooFew', params: [param] });
                let tmp = curLen - min;
                if (!tmp) {
                    reject();
                    console.warn(`The minimum quantity (${min}) has been reached, no more data can be deleted!`);
                }
                else {
                    if (sliced) {
                        resolve(data.slice(0, tmp));
                        console.warn('The amount of deleted data is too much, only part of it has been deleted!');
                    }
                    else {
                        reject();
                        console.warn('The amount of deleted data is too much, further deletion has been prevented!');
                    }
                }
            }
            else {
                resolve(null);
            }
        });
    }
    lock(cb) {
        if (this.destroyed || !this.targetEl)
            return;
        this.targetEl.toggleAttribute('inert', true);
        this.destroyed = true;
        this.listen({ name: 'locked', cb });
        return this;
    }
    unlock(cb) {
        if (!this.targetEl)
            return;
        this.targetEl.toggleAttribute('inert', false);
        this.destroyed = false;
        this.listen({ name: 'unlocked', cb });
        return this;
    }
    
    async reset(cb) {
        this.options = deepClone(this.dftOpts);
        this.revertHtml && this.targetEl && this.revertHtml(this.targetEl);
        await this.init();
        this.clearCache && this.clearCache();
        this.listen && this.listen({ name: 'reset', cb });
        return this;
    }
    
    async update(settings, cb) {
        if (this.destroyed)
            return this;
        if (!isEmpty(settings)) {
            this.updateCache && this.updateCache(settings);
            extend({ target: this.options, source: settings });
            await this.init();
        }
        this.listen && this.listen({ name: 'updated', cb, params: [settings] });
        return this;
    }
    
    async updateCont(data, cb) {
        if (this.destroyed)
            return this;
        if (this.options.hasOwnProperty('content') && !isNull(data)) {
            this.options.content = data;
            await this.init();
            this.updateCache({ content: data });
            this.listen({ name: 'updatedCont', cb, params: [data] });
            return this;
        }
    }
    
    async init(cb) { }
    
    destroy(cb) { }
}

class ModBaseListen extends ModBase {
    
    plans = {};
    
    listen({ name, params = [], cb } = {}) {
        name && this.plans.hasOwnProperty(name) ? this.emit(name, ...params) : null;
        name && this.options[`on${startUpper(name)}`] && this.options[`on${startUpper(name)}`].call(this, ...params);
        cb && cb.call(this, ...params);
    }
    
    on(type, handler) {
        plan.add(type, this, handler);
        return this;
    }
    
    emit(type, ...params) {
        plan.do(type, this, ...params);
        return this;
    }
    
    off(type, handler) {
        plan.remove(type, this, handler);
        return this;
    }
}

class Observe extends ModBaseListen {
    options = {};
    dataType;
    completedCount;
    reactCount;
    proxys;
    fullMethods;
    methods;
    output;
    canProxy;
    hasProp;
    canDeep;
    canRun;
    keys;
    types;
    proxy;
    hostProxy;
    static hostType = 'data';
    static optMaps = optObserve;
    constructor(data, options = {}, initial = true) {
        super();
        super.ready({
            type: Observe.hostType,
            options,
            maps: Observe.optMaps,
            host: data,
            spread: ['deep'],
        });
        this.proxys = [];
        this.targetData && initial && this.init();
    }
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        
        this.completedCount = 0;
        
        this.reactCount = 0;
        this.canProxy = (type, value) => (this.types.includes(type) && !isProxy(value));
        this.hasProp = (key) => (isEmpty(this.options.deep.include) || this.options.deep.include.includes(key)) && (isEmpty(this.options.deep.exclude) || !this.options.deep.exclude.includes(key));
        this.canDeep = (type, value) => this.canProxy(type, value) && this.options.deep.enable;
        this.canRun = (key) => this.options.filter ? this.options.filter(key) : true;
        
        this.keys = { got: [], set: [], deleted: [] };
        
        this.fullMethods = {
            get: (target, key, proxy) => {
                if (!this.canRun(key))
                    return;
                if (key === '_isProxy')
                    return true;
                let value = target[key], baseProps = { target, key, value, raw: value, proxy };
                !this.keys.got.includes(key) && this.keys.got.push(key);
                super.listen({ name: 'got', params: [{ ...baseProps, type: 'got' }] });
                super.listen({ name: 'crud', params: [{ ...baseProps, type: 'got' }] });
                super.listen({ name: 'trigger', params: [{ ...baseProps, type: 'got' }] });
                return Reflect.get(target, key, proxy);
            },
            set: (target, key, value, proxy) => {
                if (!this.canRun(key))
                    return true;
                if (deepEqual(target[key], value) || (this.options.lenient && target[key] === value) || (!this.options.lenient && target[key] == value))
                    return true;
                let raw = target[key], baseProps = { target, key, value, raw, proxy }, handleType = target[key] === undefined ? 'added' : 'edited';
                value = this.deepProxy(value);
                if (this.options.accept) {
                    Reflect.set(target, key, value);
                }
                !this.keys.set.includes(key) && this.keys.set.push(key);
                super.listen({ name: 'crud', params: [{ ...baseProps, type: 'set' }] });
                super.listen({ name: 'set', params: [{ ...baseProps, type: 'set' }] });
                super.listen({ name: handleType, params: [{ ...baseProps, type: handleType }] });
                super.listen({ name: 'trigger', params: [{ ...baseProps, type: 'set' }] });
                this.reactCount++;
                this.complete(this.reactCount);
                return true;
            },
            delete: (target, key) => {
                if (!this.canRun(key))
                    return true;
                let value = target[key], baseProps = { target, key, value, raw: value };
                if (this.options.accept) {
                    Reflect.deleteProperty(target, key);
                }
                !this.keys.deleted.includes(key) && this.keys.deleted.push(key);
                super.listen({ name: 'deleted', params: [{ ...baseProps, type: 'deleted' }] });
                super.listen({ name: 'crud', params: [{ ...baseProps, type: 'deleted' }] });
                super.listen({ name: 'trigger', params: [{ ...baseProps, type: 'deleted' }] });
                this.reactCount++;
                this.complete(this.reactCount);
                return true;
            },
            construct: (target, args, proxy) => {
                let baseProps = { target, args, proxy };
                super.listen({ name: 'constructed', params: [{ ...baseProps, type: 'constructed' }] });
                super.listen({ name: 'trigger', params: [{ ...baseProps, type: 'constructed' }] });
                this.reactCount++;
                this.complete(this.reactCount);
                return Reflect.construct(target, args, proxy);
            },
            apply: (target, pointer, args) => {
                let baseProps = { target, pointer, args };
                super.listen({ name: 'applied', params: [{ ...baseProps, type: 'applied' }] });
                super.listen({ name: 'trigger', params: [{ ...baseProps, type: 'applied' }] });
                this.reactCount++;
                this.complete(this.reactCount);
                return Reflect.apply(target, pointer, args);
            }
        };
        
        this.methods = {};
        for (let k in this.fullMethods) {
            let key = k === 'delete' ? 'deleteProperty' : k;
            this.methods[key] = this.fullMethods[k];
        }
        this.types = isEmpty(this.options.types) ? ['Object', 'Array', 'Function', 'Class'] : this.options.types;
        this.dataType = getDataType(this.targetData);
        if (!this.types.includes(this.dataType)) {
            console.warn(`The target data type should be an ${this.types.join('/')}, but failed to proxy the data!`);
            this.proxy = this.targetData;
        }
        else {
            
            this.hostProxy = this.options.deep.enable ? this.deepProxy(this.targetData) : this.addProxy(this.targetData);
            this.proxy = this.hostProxy;
        }
        super.listen({ name: 'initiated', cb });
        return this;
    }
    deepProxy(data) {
        let dateType = getDataType(data);
        if (this.canDeep(dateType, data)) {
            for (let k in data) {
                if (!this.hasProp(k))
                    continue;
                let val = data[k], valType = getDataType(val);
                if (data.hasOwnProperty(k) && this.canDeep(valType, val)) {
                    data[k] = this.deepProxy(val);
                }
            }
            return this.addProxy(data);
        }
        return data;
    }
    addProxy(obj) {
        if (obj['_isProxy']) {
            this.proxys.push(obj);
            return obj;
        }
        else {
            let revProxy = Proxy.revocable(obj, this.methods);
            this.proxys.push(revProxy);
            return revProxy.proxy;
        }
    }
    complete(nowReact) {
        setTimeout(() => {
            if (nowReact === this.reactCount) {
                if (this.options.once && this.completedCount !== 0)
                    return;
                for (let i in this.keys) {
                    this.keys[i] = unique(this.keys[i].filter((k) => typeof k !== 'symbol'));
                }
                super.listen({ name: 'completed', params: [{ count: this.completedCount, target: this.targetData, proxy: this.hostProxy, keys: this.keys, type: 'completed' }] });
                this.completedCount++;
                this.keys = { got: [], set: [], deleted: [] };
            }
        }, 0);
    }
    destroy() {
        this.proxys.forEach((k) => {
            k.revoke();
        });
    }
}

class ModBaseListenCache extends ModBaseListen {
    storKeys = [];
    storObs;
    storTmp;
    
    createProxy() {
        
        this.storTmp = {};
        
        this.storObs = new Observe(this.storTmp);
    }
    
    clearCache(cb) {
        if (this.options.storName) {
            for (let k in this.storObs.proxy) {
                Reflect.deleteProperty(this.storObs.proxy, k);
            }
            this.listen && this.listen({ name: 'clearedCache', cb });
            this.save();
        }
        return this;
    }
    
    updateCache(value, cb) {
        if (this.options.storName && !isEmpty(value)) {
            let temp = {};
            for (let k in value) {
                value.hasOwnProperty(k) && this.storKeys.includes(k) && (temp[k] = value[k]);
            }
            deepMerge(this.storObs.proxy, temp, {});
            this.listen && this.listen({ name: 'updatedCache', cb });
            this.save();
        }
        return this;
    }
    
    optsMergeStorage() {
        if (this.options.storName) {
            let storageVal = storage.get(this.options.storName);
            if (!isEmpty(storageVal)) {
                this.updateCache(storageVal);
                extend({ target: this.options, source: storageVal });
            }
            else {
                this.clearCache();
            }
        }
    }
    
    save(cb) {
        if (this.destroyed || !this.options.storName)
            return;
        let value = !isEmpty(this.storTmp) ? deepClone(this.storTmp) : {};
        storage.set(this.options.storName, value);
        this.listen && this.listen({ name: 'saved', cb, params: [value] });
        return this;
    }
}

const bulletTools = {
    
    getIBulletTpl: (global = {}) => {
        return `
        {{let   value = this.value?'value="'+ this.value +'"':this.label?'value="'+ this.label +'"':'',
                custom =this.custom?'<span ${alias}="custom">'+this.custom+'</span>':'',
                badge = this.badge?'<ax-badge>'+this.badge+'</ax-badge>':'',
                tips =this.tips?'<i ${alias}="tips">'+this.tips+'</i>':'',
                arrow=this.arrow?'<i class="'+this.arrow+'" ${alias}="arrow"></i>':'',
                type = this.type || "${global.type}",
                check = this.checked && !['checkboxes','radios'].includes(type) ?'checked':'',
                name = this.name?'name="'+this.name+'"':'name="${ax.namePfx}${Date.now()}"',
                icon = this.icon?'<i ${alias}="icon" class="'+this.icon+'"></i>':'',
                disk = this.disk?'<span ${alias}="disk"><img src="'+this.disk+'"></span>':'',
                cube = this.cube?'<span ${alias}="cube"><img src="'+this.cube+'"></span>':'',
                selected=this.selected?'selected':'',
                active=this.active?'active':'',
                disabled=this.disabled?'disabled':''; 
        /}}
        <${global.childNode}>
                    <div class="${prefix}bullet" ${global.unpadded ? 'unpadded' : ''} ${global.hoverable ? 'hoverable' : ''}  ${global.multiline ? 'multiline' : ''} {{value}} {{selected}} {{active}} {{check}}  {{disabled}}>
                    {{icon}}
                    {{disk}}
                    {{cube}}
                    <div class="${prefix}bullet-body">
                        {{if(type ==='checkboxes'){/}}
                            <ax-checkbox {{name}} {{this.checked?'check="ed"':''}} {{value}}>{{this.label}}</ax-checkbox>
                        {{}else if (type ==='radios'){/}}
                            <ax-radio {{name}} {{this.checked?'check="ed"':''}} {{value}}>{{this.label}}</ax-radio>
                        {{}else if(this.href){/}}
                            <a ${alias}="label" href="{{this.href}}" {{this.target?'target="'+ this.target +'"':''}} {{this.rel?'rel="'+ this.rel +'"':''}}>{{this.label}}</a>
                        {{}else{/}}
                            <span ${alias}="label">{{this.label}}</span>
                        {{}/}}
                    </div>
                    {{custom}}
                    {{badge}}
                    {{tips}}
                    {{arrow}}
                    </div>
                    ${global.divisible ? '<hr>' : ''}
                </${global.childNode}>
        `;
    },
    
    getBulletsTpl: function (global = {}) {
        let grid = '';
        if (global.cols > 0) {
            grid = `${prefix}grid  ${prefix}avg-${global.cols}`;
            grid += global.lines === 'fence' ? ` ${prefix}fence` : global.lines === 'fluid' ? ` ${prefix}fluid` : '';
        }
        return `
        <${global.parentNode} class="${prefix}reset ${grid}">
            {{for(let k of this){/}}
                ${this.getIBulletTpl(global).replaceAll('this.', 'k.')}
            {{}/}}
        </${global.parentNode}>`;
    },
    getChildNodes: (parent) => {
        let target = getEl(parent);
        if (isEmpty(target)) {
            return [];
        }
        return getEls(`.${prefix}bullet`, target);
    },
    getChecked: function (nodes) {
        let result = [];
        if (isEmpty(nodes))
            return '';
        if (nodes.length > 0) {
            if (nodes[0].querySelector('input')) {
                for (let k of nodes) {
                    let input = k.querySelector('input');
                    input.checked && result.push(input.value);
                }
            }
            else {
                for (let k of nodes)
                    k.hasAttribute('checked') && result.push(k.getAttribute('value'));
            }
        }
        return result.filter(Boolean).join(',');
    },
    setChecked: function (vals, nodes) {
        let dataType = getDataType(vals), values = [];
        if (dataType === 'Array') {
            values = vals;
        }
        else if (dataType === 'String') {
            values = vals.split(',').filter(Boolean);
        }
        if (nodes.length === 0)
            return;
        if (values.length === 0) {
            for (let k of nodes) {
                let input = k.querySelector('AX-CHECKBOX,AX-RADIO');
                input ? input.removeAttribute('check') : k.removeAttribute('checked');
            }
        }
        else {
            let checkedItems = values.map((k) => nodes.find((i) => i?.querySelector('input')?.value == k || i.getAttribute('value') == k)).filter(Boolean);
            for (let k of nodes) {
                let input = k.querySelector('AX-CHECKBOX,AX-RADIO');
                if (checkedItems.includes(k)) {
                    input ? input.setAttribute('check', 'ed') : k.toggleAttribute('checked', true);
                }
                else {
                    input ? input.removeAttribute('check') : k.removeAttribute('checked');
                }
            }
        }
    },
    toggleChecked: (item, els, type = 'select-single') => {
        if (!item || !type.includes('select') || isEmpty(els))
            return;
        let checked = item.hasAttribute('checked');
        type = type || 'select-single';
        if (type === 'select-single') {
            if (checked) {
                return;
            }
            else {
                item.toggleAttribute('checked', true);
                els.filter((k) => k !== item && item.hasAttribute('checked')).forEach((k) => {
                    k.removeAttribute('checked');
                });
            }
        }
        else {
            checked ? item.removeAttribute('checked') : item.toggleAttribute('checked', true);
        }
    },
    clearChecked: (els) => {
        if (isEmpty(els))
            return;
        for (let k of els) {
            let input = k.querySelector('AX-CHECKBOX,AX-RADIO');
            input ? input.removeAttribute('check') : k.removeAttribute('checked');
        }
    },
    toggleSelected(item, els) {
        if (!item || isEmpty(els))
            return;
        let selected = item.hasAttribute('selected');
        if (!selected) {
            item.toggleAttribute('selected', true);
            let other = els.find((k) => k !== item && k.hasAttribute('selected'));
            other && other.toggleAttribute('selected', false);
        }
    },
    setSelected(value, els) {
        if (!value || isEmpty(els))
            return;
        let item = els.find((k) => k.getAttribute('value') === value);
        item && this.toggleSelected(item, els);
    },
    getSelected(els) {
        if (isEmpty(els))
            return;
        let item = els.find((k) => k.hasAttribute('selected'));
        return item && item.getAttribute('value') || '';
    },
    clearSelected(els) {
        let item = els.find((k) => k.hasAttribute('selected'));
        item && item.removeAttribute('selected');
    }
};

const createBtns = (data, parent, settings = {}, refer) => {
    
    let target = getEl(parent), wrapEl = createEl('div', { class: `${prefix}box-btns` }), map = {
        submit: {
            name: 'submit',
            label: config.lang.button.submit,
        },
        reset: {
            name: 'reset',
            label: config.lang.button.reset,
        },
        prev: {
            name: 'prev',
            label: config.lang.button.prev,
        },
        next: {
            name: 'next',
            label: config.lang.button.next,
        },
        now: {
            name: 'now',
            label: config.lang.button.now,
        },
        close: {
            name: 'close',
            label: config.lang.button.close,
        },
        cancel: {
            name: 'cancel',
            label: config.lang.button.cancel,
        },
        clear: {
            name: 'clear',
            label: config.lang.button.clear,
        },
        confirm: {
            name: 'confirm',
            label: config.lang.button.confirm,
        },
    }, keys = Object.keys(map), render = (props) => {
        let dft = {
            label: Object.keys(config.lang.button).includes(props.name) && !props.label ? config.lang.button[props.name] : (props.label || config.lang.button.default)
        }, setProp = (type) => { type && !isNull(props[type]) && (dft[type] = props[type]); }, propArr = ['type', 'theme', 'size', 'width', 'shape', 'check', 'icon', 'tail', 'disk', 'cube', 'tips', 'mean', 'disabled', 'shaded', 'grad'];
        for (let k of propArr)
            setProp(k);
        props.name && (dft[alias] = props.name);
        props.hasOwnProperty('badge') && (dft.badge = props.badge);
        !isNull(props.href) && (dft.href = props.href);
        props.target && !isNull(props.href) && (dft.target = props.target);
        props.rel && !isNull(props.href) && (dft.rel = props.rel);
        
        props.el = createEl('ax-btn', Object.assign({ ...settings }, dft, props.attrs));
        !isEmpty(props.classes) && (classes(props.el).add(props.classes));
        !isEmpty(props.styles) && (props.el.style.cssText += props.styles);
    };
    data.forEach((k, i) => {
        let type = k?.name || k, result = keys.includes(type) ? extend({ target: { ...map[type] }, source: k.hasOwnProperty('name') ? k : {} }) : k;
        render(result);
        wrapEl.appendChild(result.el);
        result.action && (refer ? result.action.call(refer, result) : result.action(result));
        data[i] = result;
    });
    if (target) {
        let tmp = target.querySelector(`.${prefix}box-btns`);
        tmp && tmp.remove();
        target.appendChild(wrapEl);
    }
    return wrapEl;
};

const createFooter = ({ nodeName = 'div', layout = 'center', attrs, tips = false, divider = false, padding = false, children = [] }, parent, refer) => {
    
    divider = spreadBool(divider, {});
    padding = spreadBool(padding, {});
    tips = spreadBool(tips, { html: config.lang.placehold.tips });
    let target = getEl(parent), outer, footerEl = createEl(nodeName, Object.assign({ class: `${prefix}bubble-footer`, layout }, attrs)), dividerEl = divider.enable ? createEl(divider.nodeName || 'ax-line', Object.assign({ [alias]: 'divider' }, divider.attrs)) : null, paddingEl = padding.enable ? createEl(padding.nodeName || 'div', Object.assign({ class: `${prefix}p`, [alias]: 'padding' }, padding.attrs)) : null, tipsEl = tips.enable ? createEl(tips.nodeName || 'div', Object.assign({ [alias]: 'tips' }, tips.attrs), tips.html) : null, wrapEl = createEl('div', { class: `${prefix}bubble-footer-wrap` });
    footerEl.setAttribute('layout', layout);
    dividerEl && footerEl.appendChild(dividerEl);
    paddingEl ? (footerEl.appendChild(paddingEl), outer = paddingEl) : (outer = footerEl);
    outer.appendChild(wrapEl);
    if (children.length > 0) {
        createBtns(children, wrapEl, layout === 'plain' ? { type: 'plain' } : {}, refer);
    }
    tipsEl && wrapEl.appendChild(tipsEl);
    if (target) {
        let tmp = target.querySelector(`.${prefix}bubble-footer`);
        tmp && tmp.remove();
        target.appendChild(footerEl);
    }
    return footerEl;
};

const createTools = (data, parent, refer) => {
    
    let target = getEl(parent), toolsEl = createEl('span', { class: `${prefix}box-tools` }), tipsIns, map = {
        add: {
            name: 'add',
            icon: `${prefix}icon-plus`,
        },
        close: {
            name: 'close',
            icon: `${prefix}icon-close`,
        },
        remove: {
            name: 'remove',
            icon: `${prefix}icon-trash`,
        },
        edit: {
            name: 'edit',
            icon: `${prefix}icon-edit`,
        },
        update: {
            name: 'update',
            icon: `${prefix}icon-refresh`,
        },
        move: {
            name: 'move',
            icon: `${prefix}icon-drag`,
        },
        file: {
            name: 'file',
            icon: `${prefix}icon-file`,
        },
        folder: {
            name: 'folder',
            icon: `${prefix}icon-folder`,
        },
        toggle: {
            name: 'toggle',
            icon: `${prefix}icon-max`,
            swap: `${prefix}icon-min`,
        },
        enlarge: {
            name: 'enlarge',
            icon: `${prefix}icon-expand`,
            swap: `${prefix}icon-collapse`,
        },
        widen: {
            name: 'widen',
            icon: `${prefix}icon-expand-h`,
            swap: `${prefix}icon-collapse-h`,
        },
        heighten: {
            name: 'heighten',
            icon: `${prefix}icon-expand-v`,
            swap: `${prefix}icon-collapse-v`,
        }
    }, keys = Object.keys(map), renderFn = (props) => {
        let dft = {}, arrow = props.extendable ? `<i ${alias}="arrow"></i>` : '', iconStr = props.icon ? `<i class="${props.icon}" ${alias}="icon"></i>` : '', diskStr = props.disk ? `<i ${alias}="disk"><img src="${props.disk}"/></i>` : '', cubeStr = props.cube ? `<i ${alias}="cube"><img src="${props.cube}"/></i>` : '', imageStr = props.image ? `<i ${alias}="image"><img src="${props.image}"/></i>` : '', label = props.label ? `<i ${alias}="label">${props.label}</i>` : '', html = iconStr + diskStr + cubeStr + imageStr + label + arrow;
        props.name && (dft[alias] = props.name);
        props.title && (dft.title = props.title);
        props.focusable && (dft.tabindex = 1);
        props.wrapEl = createEl(props.nodeName || 'span', Object.assign(dft, props.attrs), html);
        props.iconEl = props.wrapEl.querySelector(`[${alias}="icon"]`);
        props.cubeEl = props.wrapEl.querySelector(`[${alias}="cube"]`);
        props.diskEl = props.wrapEl.querySelector(`[${alias}="disk"]`);
        props.imageEl = props.wrapEl.querySelector(`[${alias}="image"]`);
        props.labelEl = props.wrapEl.querySelector(`[${alias}="label"]`);
        !isEmpty(props.classes) && classes(props.wrapEl).add(props.classes);
        !isEmpty(props.styles) && (props.wrapEl.style.cssText += props.styles);
    };
    data.forEach((k, i) => {
        let type = k?.name || k, result = keys.includes(type) ? Object.assign(map[type], k.name ? k : {}) : k;
        renderFn(result);
        toolsEl.appendChild(result.wrapEl);
        result.action && (refer ? result.action.call(refer, result) : result.action(result));
        data[i] = result;
    });
    if (target) {
        let tmp = target.querySelector(`.${prefix}box-tools`);
        tmp && tmp.remove();
        target.appendChild(toolsEl);
    }
    let needTooltip = data.find((k) => k.tips);
    if (needTooltip) {
        toolsEl.tooltipTgr = 'pointermove';
        toolsEl.tooltipEvt = function () {
            let item = data.find((k) => k.wrapEl === this);
            if (item) {
                tipsIns.replaceCont(item.tips);
                tipsIns.transfer(this);
            }
        };
        let loadModule = async () => {
            let Mod = await Promise.resolve().then(function () { return Tooltip$1; });
            tipsIns = new Mod.default(needTooltip.wrapEl, {
                content: '',
                size: '',
                placement: 'top',
            });
            for (let k of data) {
                if (!k.tips)
                    continue;
                k.wrapEl.addEventListener(toolsEl.tooltipTgr, toolsEl.tooltipEvt, false);
            }
        };
        loadModule();
    }
    return toolsEl;
};

const clampVal = ({ val = 0, min = 0, max = 100 }) => {
    let tmpVal = toNumber(val), tmpMin = toNumber(min), tmpMax = toNumber(max), result = (!['', null, undefined].includes(min) && tmpVal < tmpMin) ? tmpMin :
        (!['', null, undefined].includes(max) && tmpVal > tmpMax) ? tmpMax : tmpVal;
    return result;
};

const isCompField = (type) => !type || typeof type !== 'string' ? false : fieldTypes.includes(type.toLowerCase().replace('ax-', ''));

const fieldTools = {
    getType: (target) => {
        let targetEl = getEl(target), result = '';
        if (!targetEl) {
            return result;
        }
        result = (['TEXTAREA', 'SELECT', 'INPUT'].includes(targetEl.nodeName)) ? 'native' :
            isCompField(targetEl.nodeName) && targetEl[ax.compSign] ? 'comp' : 'other';
        return result;
    },
    
    getVals: function ({ target, format = '', parent, separator = ',', child, key }) {
        let targetEl = getEl(target), targetType = this.getType(targetEl), values = [], none = format === 'array' ? [] : format === 'string' ? '' : null, result;
        try {
            if (targetEl) {
                let nodeName = targetEl.nodeName, fieldType = targetEl.type, fields = [], parentEl = getEl(parent) || targetEl.closest('form') || document.body;
                if (targetType === 'comp') {
                    if (['AX-CHECKBOX', 'AX-RADIO'].includes(nodeName)) {
                        fields = [...parentEl.querySelectorAll(`[name="${targetEl.name}"]`)].filter((k) => k[ax.compSign]);
                        for (let k of fields)
                            k.checked && k.value && values.push(k.value);
                        result = format === 'array' ? values : values.join(separator);
                    }
                    else {
                        let tmp = targetEl.value;
                        result = format === 'array' ? tmp.split(separator) : tmp;
                    }
                }
                else if (targetType === 'native') {
                    if (nodeName === 'SELECT') {
                        for (let k of [...targetEl.options]) {
                            k.selected && values.push(k.value || k.getAttribute('label') || '');
                        }
                    }
                    else if (fieldType === 'file') {
                        values = [...targetEl.files];
                    }
                    else if (['checkbox', 'radio'].includes(fieldType)) {
                        fields = [...parentEl.querySelectorAll(`[name="${targetEl.name}"]`)];
                        for (let k of fields)
                            k.checked && k.value && values.push(k.value);
                    }
                    else {
                        values = targetEl.value ? [targetEl.value] : [];
                    }
                    if (format === 'string') {
                        result = (fieldType === 'file') ? values.map((k) => k.name.trim()).join(separator) : values.join(separator);
                    }
                    else if (format === 'array') {
                        result = values;
                    }
                    else {
                        result = (nodeName === 'SELECT' && targetEl.multiple) || (fieldType === 'checkbox' && fields.length > 1) || (fieldType === 'file' && targetEl.multiple) ? values : (values[0] || '');
                    }
                }
                else {
                    let childEl = getEl(child, targetEl), hostEl = childEl || targetEl, tmp = key ? hostEl.getAttribute(key) : hostEl.textContent;
                    result = format === 'array' ? tmp.split(separator) : tmp;
                }
                return result;
            }
        }
        catch (err) {
            console.warn(err);
            return none;
        }
    },
    
    setVals: function ({ target, value, parent, separator = ',', child, key = '' }) {
        let targetEl = getEl(target), values = isEmpty(value) ? [] : valToArr(value, separator).map((k) => k + ''), parentEl = getEl(parent) || targetEl?.closest('form') || document.body, fields = [];
        if (targetEl) {
            let targetType = this.getType(targetEl);
            if (targetType === 'comp') {
                let value = values.join(separator);
                if (['AX-CHECKBOXES', 'AX-RADIOS'].includes(targetEl.nodeName)) {
                    targetEl.setAttribute('checked', value);
                }
                else if (['AX-CHECKBOX', 'AX-RADIO'].includes(targetEl.nodeName)) {
                    let fields = [...parentEl.querySelectorAll(`[name="${targetEl.name}"]`)].filter((k) => k[ax.compSign] && k.getAttribute('value'));
                    for (let k of fields) {
                        values.includes(k.value) ? k.setAttribute('check', 'ed') : k.removeAttribute('check');
                    }
                }
                else {
                    targetEl.setAttribute('value', value);
                }
            }
            else if (targetType === 'native') {
                let nodeName = targetEl.nodeName, fieldType = targetEl.type;
                if (nodeName === 'SELECT') {
                    let options = [...targetEl.options], tmp = values.map((k) => k.toString());
                    options.forEach((k) => {
                        let value = k.value || k.getAttribute('label') || '';
                        k.selected = tmp.includes(value) ? true : false;
                    });
                    fields = [targetEl];
                }
                else if (['checkbox', 'radio'].includes(fieldType)) {
                    fields = [...parentEl.querySelectorAll(`[name="${targetEl.name}"]`)].filter((k) => k.getAttribute('value'));
                    let tmp = values.map((k) => k.toString());
                    fields.forEach((k) => {
                        k.checked = tmp.includes(k.value) ? true : false;
                    });
                }
                else if (fieldType === 'file') {
                    targetEl.value = '';
                    console.warn('The field.files property cannot be changed through scripts,this operation is performed by resetting!');
                }
                else if (fieldType === 'number') {
                    targetEl.value = clampVal({
                        val: values[0] || 0,
                        min: targetEl.min,
                        max: targetEl.max
                    });
                }
                else if (fieldType === 'range') {
                    targetEl.value = values[0] || targetEl.min || 0;
                }
                else {
                    targetEl.value = values.filter(Boolean).join();
                }
            }
            else {
                let childEl = getEl(child, targetEl), hostEl = childEl || targetEl;
                key ? hostEl.setAttribute(key, value) : hostEl.innerHTML = value;
            }
        }
    },
    
    reset: function ({ target, parent, zero = false }) {
        let fieldEl = getEl(target);
        if (!fieldEl) {
            return;
        }
        let fielType = this.getType(fieldEl), type = fieldEl.type;
        if (fielType === 'native') {
            if (type?.includes('select')) {
                [...fieldEl?.options].forEach((i) => i.selected = !zero ? i.defaultSelected : false);
            }
            else if (type === 'checkbox' || type === 'radio') {
                let parentEl = getEl(parent) || fieldEl?.closest('form') || document.body, fields = [...parentEl.querySelectorAll(`[name="${fieldEl?.name}"]`)];
                fields.forEach((k) => {
                    k.checked = !zero ? k.defaultChecked : false;
                });
            }
            else if (['number', 'range'].includes(type)) {
                fieldEl.value = !zero ? fieldEl?.defaultValue : (fieldEl?.min || 0);
            }
            else {
                fieldEl.value = !zero ? fieldEl?.defaultValue : '';
            }
        }
        else if (fielType === 'comp') {
            zero ? fieldEl.clear() : fieldEl.reset();
        }
        else {
            if (zero && fieldEl.clear) {
                fieldEl.clear();
            }
            else if (!zero && fieldEl.reset) {
                fieldEl.reset();
            }
        }
    }
};

const tplToEl = (tpl) => {
    tpl = tpl.trim().replaceAll('&lt;', '<').replaceAll('&gt;', '>');
    if (!tpl)
        return null;
    let node = '';
    if (tpl.startsWith('<tr') || tpl.startsWith('<TR')) {
        node = 'tr';
        tpl = `<table><tbody>${tpl}</tbody></table>`;
    }
    else if (tpl.startsWith('<td') || tpl.startsWith('<TD')) {
        node = 'td';
        tpl = `<table><tbody><tr>${tpl}</tr></tbody></table>`;
    }
    let tplNode = createEl('TEMPLATE', '', tpl), clone = tplNode.content.cloneNode(true), elem = node ? clone.querySelector(node) : clone.firstElementChild;
    return elem || null;
};

const optMessage = [
    {
        attr: 'heading',
        prop: 'heading',
        value: '',
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'placement',
        prop: 'placement',
        value: 'center-top',
    },
    {
        attr: 'content',
        prop: 'content',
        value: '',
    },
    {
        attr: 'status',
        prop: 'status',
        value: 'info',
    },
    {
        attr: 'delay',
        prop: 'delay',
        value: 3000,
    },
    {
        attr: 'progress',
        prop: 'progress',
        value: true,
    },
    {
        attr: 'closable',
        prop: 'closable',
        value: true,
    },
    {
        attr: 'manual',
        prop: 'manual',
        value: false,
    },
    {
        attr: 'eager',
        prop: 'eager',
        value: false,
    },
    {
        attr: 'icon-show',
        prop: 'iconShow',
        value: false,
    },
    {
        attr: 'notable',
        prop: 'notable',
        value: false,
    },
    {
        attr: 'z-index',
        prop: 'zIndex',
        value: 0,
    },
    {
        attr: 'on-shown',
        prop: 'onShown',
        value: null,
    },
    {
        attr: 'on-hidden',
        prop: 'onHidden',
        value: null,
    },
    ...optBase
];

class Message extends ModBaseListen {
    options = {};
    parentEl;
    template;
    shown;
    static hostType = 'none';
    static optMaps = optMessage;
    constructor(options, initial = true) {
        super();
        super.ready({
            options,
            type: Message.hostType,
            maps: Message.optMaps,
        });
        
        
        
        
        
        
        
        
        this.parentEl = null;
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.createParent(this.options.placement || 'center-top');
        this.createSection();
        this.shown = false;
        elState(this.parentEl).isVirtual && document.body.appendChild(this.parentEl);
        super.listen({ name: 'initiated', cb });
        this.options.eager && this.show();
        return this;
    }
    createParent = (placement) => {
        let parent = ax.messages.find((k) => k.placement === placement);
        if (!parent) {
            this.parentEl = createEl('div', { class: `${prefix}message`, placement });
            this.options.classes && classes(this.parentEl).add(this.options.classes);
            ax.messages.push({ placement, el: this.parentEl });
        }
        else {
            this.parentEl = parent.el;
        }
        this.options.zIndex && (this.parentEl.style.zIndex = this.options.zIndex);
    };
    createSection() {
        this.template = `
        <ax-callout size="lg" opaque hidden content="${this.options.content === true || this.options.content === '' ? this.options.lang.content[this.options.status] : this.options.content}"
        ${this.options.notable ? 'notable' : ''}
        ${this.options.iconShow ? 'result' : ''}
        ${this.options.closable ? 'closable' : ''}
        ${this.options.status ? 'theme="' + (this.options.status || 'info') + '"' : ''}
        ${this.options.heading ? 'caption="' + (this.options.heading === true ? this.options.lang.heading[this.options.status] : this.options.heading) + '"' : ''}
        ${!this.options.progress ? 'noprogress' : ''}
        >
        </ax-callout>
        `;
        this.targetEl = tplToEl(this.template);
    }
    getCloseBtns() {
        for (let k of getEls(`[${alias}="closebubble"]`, this.targetEl))
            k.onclick = () => this.hide();
    }
    
    show(cb) {
        if (this.destroyed || this.shown)
            return this;
        this.parentEl.appendChild(this.targetEl);
        this.targetEl.toggleAttribute('hidden', false);
        this.getCloseBtns();
        this.targetEl.on('hidden', () => {
            this.targetEl.remove();
            this.shown = false;
            super.listen({ name: 'hidden' });
        });
        this.targetEl.on('shown', () => {
            this.shown = true;
            super.listen({ name: 'shown' });
        });
        !this.options.manual && this.targetEl.setAttribute('autoclose', this.options.delay);
        return this;
    }
    
    hide(cb) {
        if (this.destroyed || !this.shown)
            return this;
        this.targetEl.toggleAttribute('hidden', true);
        return this;
    }
    updateCont(content, cb) {
        if (this.destroyed)
            return this;
        this.targetEl.setAttribute('content', content);
        this.getCloseBtns();
        super.listen({ name: 'updatedCont', cb, params: [content] });
        return this;
    }
    
    async update(settings, cb) {
        if (this.destroyed)
            return this;
        this.targetEl.remove();
        this.options = extend({ target: this.options, source: settings });
        await this.init();
        super.listen({ name: 'updated', cb, params: [settings] });
        return this;
    }
    
    destroy(cb) {
        if (this.destroyed)
            return this;
        this.targetEl.remove();
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

const combineArr = ({ origin, length = 2, start = 0, splice = [], result = [] }) => {
    let arrayLen = origin.length;
    if (start + length > arrayLen) {
        return [];
    }
    for (let i = start; i < arrayLen; i++) {
        if (length === 1) {
            result.push([...splice, origin[i]]);
            i + 1 < arrayLen && combineArr({ origin, length, start: i + 1, splice, result });
            break;
        }
        combineArr({ origin, length: length - 1, start: i + 1, splice: [...splice, origin[i]], result });
    }
    return result || [];
};

const sliceFrags = ({ str = '', start = '{', end = '}', contain = true }) => {
    let reg = new RegExp(`\\${start}([^\\${end}]+)\\${end}`, 'g'), arr = str.match(reg) || [];
    return (!contain && arr.length > 0) ? arr.map((k) => k.replace(start, '').replace(end, '')) : arr;
};

const toLocalTime = (date) => {
    let dateStr, localDate, flag, loacalTime;
    if (date) {
        date = date.trim();
        localDate = new Date(date);
        flag = localDate instanceof Date && !isNaN(localDate.getTime());
        if (!flag) {
            throw new Error('Date format must be "YYYY-MM-DD" or "YYYY/MM/DD"!');
        }
        dateStr = date.replace(/-/g, "/");
        loacalTime = new Date(dateStr).getTime();
    }
    else {
        loacalTime = Date.now();
    }
    return loacalTime;
};

const validTools = {
    
    test: (value, pattern) => {
        if (isEmpty(value) || isEmpty(pattern)) {
            return false;
        }
        return (typeof pattern === 'string' ? new RegExp(pattern) : pattern).test(value);
    },
    
    expired: (value, data) => {
        let getTime = (v) => typeof v === 'string' ? toLocalTime(v) : v.getTime(), valueTime = getTime(value), dataType = getDataType(data), result = [-2, -2], compare = (a, b) => {
            return a < b ? -1 :
                a === b ? 0 :
                    a > b ? 1 : -2;
        };
        if (isEmpty(value) || isEmpty(data)) {
            return result;
        }
        if (dataType === 'Array' && data.length === 2) {
            let dates = data.map((k) => getTime(k)).sort();
            result[0] = compare(valueTime, dates[0]);
            result[1] = compare(valueTime, dates[1]);
        }
        else {
            let dataTime = getTime(data);
            result[0] = compare(valueTime, dataTime);
        }
        return result;
    },
    
    than: (value, data) => {
        let result = [-2, -2], dataType = getDataType(data), valueParse = parseFloat(value), dataParse, compare = (a, b) => {
            return a < b ? -1 :
                a === b ? 0 :
                    a > b ? 1 : -2;
        };
        if (dataType === 'Array') {
            if (data.length === 2) {
                dataParse = data.map((k) => parseFloat(k)).sort();
                result[0] = compare(valueParse, dataParse[0]);
                result[1] = compare(valueParse, dataParse[1]);
            }
        }
        else {
            dataParse = parseFloat(data);
            result[0] = compare(valueParse, dataParse);
        }
        return result;
    },
    
    getStrength: (value, data) => {
        let datas = Object.assign({ lowerCase: '[a-z]', upperCase: '[A-Z]', digital: '[0-9]', chars: `[${config.valid.regChars}]`, local: `[${config.lang.valid.regLocal}]`, length: config.valid.lengthStr }, data), strength = 0;
        if (isEmpty(value) || typeof value !== 'string') {
            return strength;
        }
        for (let k in datas) {
            if (k === 'length') {
                value.trim().length >= datas[k] ? strength++ : null;
            }
            else {
                if (datas.hasOwnProperty(k) && datas[k]) {
                    value.match(new RegExp(datas[k])) ? strength++ : null;
                }
            }
        }
        return strength;
    },
    
    parseLength: (value) => {
        let regex = '', text = '';
        if (value) {
            let arr = value.replace('{', '').replace('}', '').split(',').map((k) => ~~k);
            if (arr.length === 2) {
                regex = (arr[1] && arr[1] > arr[0]) ? `{${arr[0]},${arr[1]}}` :
                    (arr[1] && arr[1] === arr[0]) ? `{${arr[0]}}` : `{${arr[0]},}`;
                if (arr[1]) {
                    if (arr[0] === 0) {
                        text = renderTpl(config.lang.valid['length<='], { label: '', data: arr[1] });
                    }
                    else {
                        text = renderTpl(config.lang.valid['length>=<='], { label: '', data: arr });
                    }
                }
                else {
                    text = renderTpl(config.lang.valid['length>='], { label: '', data: arr[0] });
                }
            }
            else {
                regex = `{${arr[0]}}`;
                text = renderTpl(config.lang.valid['length='], { label: '', data: arr[0] });
            }
        }
        return { regex, text };
    },
    
    parseSpecific: function (value, data) {
        value = trim(value, 'global');
        let regex = '', typeStr = value, rangeStr = '', text = '', strData = {}, regTypes = Object.assign({ ...config.lang.valid.types }, data?.types), tplStr = data?.tpl || config.lang.valid.specific, tplData = {};
        if (value) {
            let rangeTemp = sliceFrags({ str: value });
            if (rangeTemp.length > 0) {
                rangeStr = rangeTemp[0];
                typeStr = value.replace(rangeStr, '');
            }
            regex = '^';
            let arr = typeStr.split('&');
            arr.forEach((k) => {
                let val = k.split('='), reg = '';
                reg = (val[0] === 'a') ? `.*?[a-z]` :
                    (val[0] === 'A') ? `.*?[A-Z]` :
                        (val[0] === 'd') ? `.*?[0-9]` :
                            (val[0] === '~') ? `.*?[${data?.regChars || config.valid.regChars}]` :
                                (val[0] === '@') ? `.*?[${data?.regLocal || config.lang.valid.regLocal}]` : '';
                regex += (reg ? '(?=' + Array((val[1] ? ~~val[1] : 1) + 1).join(reg) + ')' : '');
                strData[regTypes[val[0]]] = val[1] || 1;
            });
            tplData = { label: data?.label || '', name: data?.name || '', value: data?.value || '', data: strData };
            regex += `.${this.parseLength(rangeStr).regex || '{0,}'}$`;
            text = renderTpl(tplStr, tplData) + (rangeStr ? this.parseLength(rangeStr).text : '');
        }
        return { regex, text };
    },
    
    parseCombine: function (value, data) {
        value = trim(value, 'global');
        let regChars = data?.regChars || config.valid.regChars, regLocal = data?.regLocal || config.lang.valid.regLocal, regex = '', typeStr = value, rangeStr = '', text = '', strData = { types: [], total: 0 }, regTypes = Object.assign({ ...config.lang.valid.types }, data?.types), tplStr = data?.tpl || config.lang.valid.combine, tplData = {};
        if (value) {
            let rangeTemp = sliceFrags({ str: value });
            if (rangeTemp.length > 0) {
                rangeStr = rangeTemp[0];
                typeStr = value.replace(rangeStr, '');
            }
            let arr = typeStr.split(':'), types = ~~arr[1] ? ~~arr[1] : 2, regs = [];
            arr[0].includes('a') ? (regs.push('a-z'), strData.types.push(regTypes['a'])) : null;
            arr[0].includes('A') ? (regs.push('A-Z'), strData.types.push(regTypes['A'])) : null;
            arr[0].includes('d') ? (regs.push('0-9'), strData.types.push(regTypes['d'])) : null;
            arr[0].includes('~') ? (regs.push(regChars), strData.types.push(regTypes['~'])) : null;
            arr[0].includes('@') ? (regs.push(regLocal), strData.types.push(regTypes['@'])) : null;
            types = Math.min(types, regs.length);
            if (types === 1) {
                regex = `^[${regs.join('')}]`;
            }
            else {
                regex += '^';
                combineArr({ origin: regs, length: types - 1 }).forEach((k) => {
                    regex += `(?![${k.join('')}]+$)`;
                });
                regex += `[${regs.join('')}]`;
            }
            strData.total = types;
            tplData = { label: data?.label || '', name: data?.name || '', value: data?.value || '', data: strData };
            regex += `${this.parseLength(rangeStr).regex || '{0,}'}$`;
            text = renderTpl(tplStr, tplData) + (rangeStr ? this.parseLength(rangeStr).text : '');
        }
        return { regex, text };
    },
    
    validForm: async ({ el, fail, succ, intvl = 100 }) => {
        let parentEl = getEl(el) || document.body, validItem = ax.valids.find((k) => k.parent === parentEl), children = validItem?.children || [], passed = true, fails = [], loopValid = async () => {
            for (let i = 0; i < children.length; i++) {
                let v = children[i];
                await delay({
                    duration: intvl * i
                }).then(async () => {
                    await v.ins.do().then((res) => {
                        !res.passed ? (passed = res.passed, fails.push(v)) : null;
                    });
                });
            }
        };
        if (ax.valids.length === 0 || !validItem) {
            new Message({
                content: config.lang.valid.message.noValids,
                iconShow: true,
                status: 'info',
            }).show();
            return { passed: true, fails: [] };
        }
        await loopValid().then(() => {
            !passed ? (fail && fail(fails)) : (succ && succ());
        });
        return { passed, fails };
    },
    
    listenSubmit: async function ({ item, fail, succ, cb }) {
        let allItems = ax.valids, getItems = () => {
            let itemType = getDataType(item), result = [];
            if (isEmpty(item)) {
                result = allItems;
            }
            else {
                if (itemType === 'Array') {
                    result = item;
                }
                else {
                    if (itemType.includes('HTML')) {
                        result = [allItems.find((k) => k.parent === item)];
                    }
                    else if (itemType === 'String') {
                        let temp = getEl(item);
                        temp ? result = [allItems.find((k) => k.parent === temp)] : null;
                    }
                    else {
                        result = [item];
                    }
                }
            }
            return result;
        }, usableItems = getItems().filter((k) => k?.parent?.nodeName === 'FORM'), result = { passed: true, fails: [] };
        if (usableItems.length > 0) {
            for (let k of usableItems) {
                await this.validForm({
                    el: k.parent,
                    fail: (fails) => {
                        fail && fail(k, fails);
                    },
                    succ: (fails) => {
                        if (succ) {
                            succ(k, fails);
                        }
                        else {
                            k.parent.submit();
                        }
                    }
                }).then((resp) => {
                    result = resp;
                    cb && cb(k, resp);
                });
            }
        }
        return result;
    },
    
    factory: async (data) => {
        let textSucc = data?.text?.succ || config.lang.valid.succ, textFail = data?.text?.fail || config.lang.valid.fail, result = { passed: true, succ: renderTpl(textSucc, { label: data.label, name: data.name }), fail: renderTpl(textFail, { label: data.label, name: data.name }) };
        if (data.rules.length === 0) {
            return result;
        }
        try {
            for (let k in data.rules) {
                if (data.rules.hasOwnProperty(k)) {
                    let item = data.rules[k], param = { value: data.value, name: data.name, label: data.label, type: k, ins: data.ins, data: data.ins.types[k] }, output = await item(param), dataType = getDataType(output);
                    if (dataType === 'Object') {
                        Object.assign(result, output?.content?.hasOwnProperty('passed') ? output.content : output);
                        if (!result.passed)
                            break;
                    }
                    else {
                        throw new Error('Return value is not a standard object : {passed,succ,fail}!');
                    }
                }
            }
        }
        catch (err) {
            result.passed = false;
            new Message({
                status: 'info',
                content: config.lang.valid.message.wrongRule,
            }).show();
            console.error(err);
        }
        data.cb && data.cb(result);
        return result;
    }
};

const formTools = {
    
    getName: (item) => {
        let field = getEl(item), types = ['file', 'select-multiple', 'checkbox'];
        return (types.includes(field.type) && !field.name.endsWith('[]')) ? `${field.name}[]` : field.name;
    },
    
    getHosts: (el) => {
        let form = getEl(el), condition = (k) => k.name && !k.name.includes(ax.namePfx) && !k.hasAttribute(ax.embedSign);
        if (!form)
            return [];
        return unique([...form.querySelectorAll(`${fieldTypes.map((k) => 'ax-' + k).join()},input:not([type="submit"],[type="reset"]),select,textarea`)].filter(condition), 'name');
    },
    
    reset: function (el, zero = false) {
        let parentEl = getEl(el), fields = this.getHosts(parentEl);
        if (fields.length > 0) {
            for (let k of fields) {
                fieldTools.reset({ target: k, parent: parentEl, zero });
            }
        }
    },
    
    setVals: function (el, data = {}) {
        let parentEl = getEl(el), fields = this.getHosts(parentEl);
        if (!parent || fields.length === 0) {
            return;
        }
        if (isEmpty(data))
            this.reset(parent, true);
        for (let [k, value] of Object.entries(data)) {
            let item = fields.find((i) => i.name === k);
            item && fieldTools.setVals({ target: item, value, parent });
        }
    },
    
    getVals: function (el, format = 'array') {
        let parentEl = getEl(el), none = format === 'string' ? '' : format === 'json' ? {} : format === 'formdata' ? new FormData() : [], fields = [], items = [], output;
        try {
            if (parentEl) {
                fields = this.getHosts(parentEl);
                items = fields.map((k) => {
                    let value = fieldTools.getVals({ target: k, parent: parentEl }), fullName = k.name + (Array.isArray(value) && !k.name.endsWith('[]') ? '[]' : '');
                    return { type: k.type, name: k.name, fullName, value };
                });
                if (format === 'string') {
                    output = '';
                    items.forEach((k, i) => {
                        let symbol = i === 0 ? '' : '&';
                        if (Array.isArray(k.value)) {
                            if (k.value.length > 0) {
                                for (let x of k.value)
                                    output += symbol + k.fullName + '=' + (getDataType(k) === 'File' ? x.name : x);
                            }
                            else {
                                output += symbol + k.fullName + '=';
                            }
                        }
                        else {
                            output += symbol + k.name + '=' + (getDataType(k.value) === 'File' ? k.value.name : k.value);
                        }
                    });
                }
                else if (format === 'formdata') {
                    output = new FormData();
                    for (let k of items) {
                        if (Array.isArray(k.value)) {
                            if (k.value.length > 0) {
                                for (let x of k.value)
                                    output.append(k.fullName, x);
                            }
                        }
                        else {
                            output.append(k.name, k.value);
                        }
                    }
                    ;
                }
                else if (format === 'json') {
                    output = {};
                    for (let k of items)
                        output[k.name] = k.value;
                }
                else {
                    output = items;
                }
                return output;
            }
            else {
                return none;
            }
        }
        catch {
            console.warn('Failed to retrieve data from the form, and empty content has been output!');
            return none;
        }
    },
    
    submit: async function (el, options) {
        let formEl = getEl(el);
        if (!formEl)
            throw new Error('The form node is required!');
        let opt = extend({
            target: {
                type: 'post',
                delay: 0,
                
            },
            source: options
        }), ajaxSubmit = async () => {
            if (!opt.url)
                throw new Error('The request url is required!');
            let btnEl = getEl(opt.button?.selector), btnText = btnEl ? btnEl.innerHTML : '', resume = () => {
                formEl?.classList.remove(`${prefix}ajax-mask`);
                if (btnEl) {
                    if (opt?.button?.replace) {
                        btnEl.removeAttribute('inert');
                        btnEl.innerHTML = btnText;
                    }
                    else {
                        opt?.button?.attr && btnEl.removeAttribute(opt.button.attr.key);
                    }
                }
            };
            if (formEl?.classList.contains(`${prefix}ajax-mask`))
                return;
            await ajax({
                url: opt.url,
                data: this.getVals(formEl, opt.type === 'get' ? 'string' : 'formdata'),
                type: opt.type,
                opened: (response) => {
                    opt.opened && opt.opened(response);
                },
                before: (response) => {
                    formEl?.classList.add(`${prefix}ajax-mask`);
                    if (btnEl) {
                        if (opt?.button?.replace === 'loading') {
                            btnEl.innerHTML = response.content;
                            btnEl.toggleAttribute('inert', true);
                        }
                        else if (opt.button?.replace === 'text') {
                            btnEl.innerHTML = config.lang.ajax.submit.btn;
                            btnEl.toggleAttribute('inert', true);
                        }
                        else {
                            opt?.button?.attr && btnEl.setAttribute(opt.button.attr.key, opt.button.attr.value);
                        }
                    }
                    opt.before && opt.before(response);
                },
                success: (response) => {
                    setTimeout(() => {
                        resume();
                        new Message({
                            content: config.lang.ajax.submit.succ,
                            status: 'succ',
                            iconShow: true,
                        }).show();
                        opt.success && opt.success(response);
                    }, opt.delay);
                },
                error: (response) => {
                    resume();
                    new Message({
                        content: config.lang.ajax.submit.fail,
                        status: 'error',
                        iconShow: true,
                    }).show();
                    opt.error && opt.error(response);
                }
            });
        }, submitForm = async () => {
            if (opt.url) {
                ajaxSubmit();
            }
            else {
                formEl?.nodeName === 'FORM' && formEl.submit();
            }
        }, awaitValid = async () => {
            if (opt.valid) {
                let resp = await opt.valid();
                resp && submitForm();
            }
            else {
                submitForm();
            }
        }, validItem = ax.valids.find((k) => k.parent === formEl);
        if (validItem) {
            await validTools.validForm({
                el: formEl,
                fail: () => {
                    opt.invalid && opt.invalid();
                },
                succ: async () => {
                    await awaitValid();
                }
            });
        }
        else {
            await awaitValid();
        }
    }
};

const setContent = ({ content, target, position = 'override', engine, template, prevent }) => {
    let contType = getDataType(content), el = getEl(target), result = '';
    if (isNull(content))
        return;
    if (contType.includes('HTML') || contType === 'String' || contType === 'Number') {
        result = content;
    }
    else if (contType === 'Array' || contType === 'Object') {
        let tplDom = getEl(template), tplStr = tplDom?.innerHTML || template, tplEng = engine || renderTpl;
        if (tplStr && tplEng) {
            try {
                result = tplEng.name === 'template' ? tplEng(tplStr)(content) : tplEng(tplStr, content);
            }
            catch (err) {
                console.error(err);
            }
        }
        else {
            console.warn('Be sure to fill in the template content and template engine!');
        }
    }
    if (!isEmpty(prevent)) {
        prevent(result);
    }
    else {
        if (!el)
            return;
        let valType = getDataType(result);
        if (valType.includes('HTML')) {
            (position === 'override') ? (el.innerHTML = '', el.appendChild(content)) : el.insertAdjacentElement(position, content);
        }
        else {
            (position === 'override') ? el.innerHTML = result : el.insertAdjacentHTML(position, result);
        }
    }
};

class ModBaseListenCacheBubble extends ModBaseListenCache {
    bubbleType;
    positionIns;
    hoverIns;
    mainEl;
    state;
    footEl;
    data;
    submitXhr;
    contEl;
    template;
    wrapEl;
    bodyEl;
    headEl;
    maskEl;
    toggleShow;
    triggerClose;
    contXhr;
    triggerShow;
    wings;
    static hostType = 'node';
    
    formEl = null;
    canTrigger = false;
    lastShowTime = 0;
    bullets = { nodes: [], value: '' };
    setEmpty() {
        if (this.bubbleType === 'popup') {
            this.positionIns && this.positionIns.destroy();
            this.hoverIns && this.hoverIns.destroy();
        }
        this.mainEl && this.mainEl.remove();
        
        this.state = 'hidden';
    }
    async hide(param) { }
    handleFooter() {
        if (this.footEl) {
            let isBullets = this.options.bullet.enable && ['checkboxes', 'radios', 'select-single', 'select-multi'].includes(this.options.bullet.type);
            this.options.footer.children.forEach((k) => {
                if (k.name === 'confirm') {
                    if (k.action) {
                        k.action.call(this, k);
                    }
                    else {
                        if (!k?.attrs?.href) {
                            k.el.onclick = async (e) => {
                                this.options.b4Confirm && await this.options.b4Confirm.call(this, k);
                                let inputEl = getEl(this.options.autoFill.inputSel) || this.targetEl, childEl = getEl(this.options.autoFill.childSel, inputEl), vals;
                                if (isCompField(this.options.contType)) {
                                    let value = this.data.value;
                                    this.options.autoFill.enable && inputEl && this.setTargetVals(value, inputEl, childEl);
                                    !this.options.keepShow && this.hide();
                                    vals = value;
                                }
                                else if (isBullets) {
                                    if (this.targetEl) {
                                        this.bullets.value = this.getBulletsChecked();
                                        this.setTargetVals(this.bullets.value, this.targetEl, childEl);
                                    }
                                    !this.options.keepShow && this.hide();
                                    vals = this.bullets.value;
                                }
                                else {
                                    if (k.event === 'submit') {
                                        if (this.formEl) {
                                            formTools.submit(this.formEl, {
                                                url: k.url,
                                                invalid: () => {
                                                    this.listen({ name: 'invalidated' });
                                                },
                                                valid: () => {
                                                    this.listen({ name: 'validated' });
                                                }
                                            });
                                        }
                                        else {
                                            throw new Error('Form node is required!');
                                        }
                                    }
                                    else if (k.event === 'async') {
                                        if (this.formEl && k.url) {
                                            formTools.submit(this.formEl, {
                                                url: k.url,
                                                button: {
                                                    selector: k.el,
                                                    attr: { key: 'check', value: 'ing' },
                                                },
                                                opened: (resp) => {
                                                    this.submitXhr = resp.xhr;
                                                },
                                                invalid: () => {
                                                    this.listen({ name: 'invalidated' });
                                                },
                                                valid: () => {
                                                    this.listen({ name: 'validated' });
                                                },
                                                success: (resp) => {
                                                    this.listen({ name: 'sended', params: [resp.content] });
                                                }
                                            });
                                        }
                                        else {
                                            throw new Error('The request url and form node are required!');
                                        }
                                    }
                                    else if (k.event === 'prevent') {
                                        preventDft(e);
                                    }
                                    else {
                                        !this.options.keepShow && this.hide();
                                    }
                                }
                                this.listen({ name: 'confirmed', cb: k.cb, params: [k, vals] });
                            };
                        }
                    }
                }
                else if (k.name === 'cancel') {
                    if (k.action) {
                        k.action.call(this, k);
                    }
                    else {
                        if (!k?.attrs?.href) {
                            k.el.onclick = async (e) => {
                                this.options.b4Cancel && await this.options.b4Cancel.call(this, k);
                                if (k.event === 'zero') {
                                    this.formEl && formTools.reset(this.formEl, true);
                                }
                                else if (k.event === 'reset') {
                                    this.formEl && formTools.reset(this.formEl, false);
                                }
                                else if (k.event === 'prevent') {
                                    preventDft(e);
                                }
                                else {
                                    !this.options.keepShow && this.hide();
                                }
                                this.listen({ name: 'canceled', cb: k.cb, params: [k] });
                            };
                        }
                    }
                }
                else if (k.name === 'close') {
                    if (k.action) {
                        k.action.call(this, k);
                    }
                    else {
                        k.el.onclick = () => {
                            this.hide();
                        };
                    }
                    this.listen({ name: 'closed', cb: k.cb, params: [k] });
                }
                else if (k.name === 'clear') {
                    if (k.action) {
                        k.action.call(this, k);
                    }
                    else {
                        k.el.onclick = () => {
                            if (isCompField(this.options.contType)) {
                                fieldTools.reset({ target: this.data, zero: true });
                            }
                            else if (isBullets) {
                                this.clearBulletsChecked();
                            }
                            this.listen({ name: 'cleared', cb: k.cb, params: [k] });
                        };
                    }
                }
                else {
                    k.action && k.action.call(this, k);
                }
            });
        }
    }
    getBulletsTpl() {
        return !this.options.tplStr && this.options.bullet.enable ? bulletTools.getBulletsTpl(this.options.bullet) : '';
    }
    handleData(data) {
        let result = data;
        if (!this.options.bullet.enable)
            return result;
        if (Array.isArray(data) && typeof data[0] === 'string') {
            result = data.map((k) => { return { label: k }; });
        }
        else if (typeof data === 'string' && !data.includes(`${prefix}bullet-body`)) {
            result = data.split(',').filter(Boolean).map((k) => { return { label: k }; });
        }
        return result;
    }
    async renderContent(data) {
        isEmpty(data) && console.warn('No insertable content!');
        this.data = this.handleData(data);
        if (this.options.b4Fill) {
            let resp = await this.options.b4Fill.call(this, this.data, this.contEl);
            resp && (this.data = resp);
        }
        this.listen({ name: 'render', params: [this.data] });
        let dataType = getDataType(this.data), tplEl = getEl(this.options.tplStr), template = tplEl?.innerHTML || this.options.tplStr || this.getBulletsTpl();
        if (dataType.includes('HTML') && ['image', 'iframe', 'audio', 'video'].includes(this.options.contType)) {
            this.options.media.caption && this.data.insertAdjacentHTML('afterbegin', `<div ${alias}="caption">${this.options.media.caption}</div>`);
            this.options.media.brief && this.data.insertAdjacentHTML('beforeend', `<div ${alias}="brief">${this.options.media.brief}</div>`);
        }
        setContent({
            content: this.data,
            target: this.contEl,
            engine: this.options.tplEng,
            template,
        });
        this.formEl = this.contEl.querySelector('form');
        this.listen({ name: 'rendered', params: [this.data] });
    }
    renderBubble(name = 'popup') {
        
        this.template = renderTpl(this.getTpl(name), this.options);
        
        this.mainEl = tplToEl(this.template);
        
        this.wrapEl = this.mainEl.querySelector(`.${prefix}${name}-wrap`);
        
        this.contEl = this.mainEl.querySelector(`.${prefix}${name}-cont`);
        
        this.bodyEl = this.mainEl.querySelector(`.${prefix}${name}-body`);
        
        this.headEl = this.mainEl.querySelector(`.${prefix}${name}-head`);
        
        this.footEl = this.mainEl.querySelector(`.${prefix}${name}-foot`);
        
        this.maskEl = this.mainEl.querySelector(`.${prefix}${name}-mask`);
        if (this.options.tools.enable) {
            createTools(this.options.tools.children, this.wrapEl, this);
        }
        if (this.footEl) {
            if (this.options.footer.layout !== 'plain') {
                this.options.footer.children = this.options.footer.children.map((k) => {
                    if (k === 'confirm') {
                        return { name: 'confirm', attrs: { theme: 'prim' } };
                    }
                    else if (k?.name === 'confirm') {
                        if (k.attrs) {
                            !k.attrs.theme && (k.attrs.theme = 'prim');
                        }
                        else {
                            k.attrs = { theme: 'prim' };
                        }
                        return k;
                    }
                    else {
                        return k;
                    }
                });
            }
            createFooter(this.options.footer, this.footEl, this);
        }
    }
    getTpl(name = 'popup') {
        return `
                            {{let noPadding = (this.padding.enable  && !this.bullet.enable) || (this.padding.enable && this.bullet.enable && this.bullet.cols>0 && this.bullet.lines==='fluid');/}}
                            <div class="${prefix}${name}">
                                {{ if(this?.mask?.enable){ /}}<div class="${prefix}${name}-mask"></div>{{ } /}}
                                <div class="${prefix}${name}-wrap">
                                    {{ if(this.heading){ /}}
                                        <div class="${prefix}${name}-head">{{ this.heading }}</div>
                                        {{ } /}}
                                    {{ if(this.divider){ /}}
                                        <ax-line></ax-line>
                                        {{ } /}}
                                    <div class="${prefix}${name}-body">
                                        {{ if(this.padding.enable){ /}}
                                            <div class="${prefix}${name}-padding {{ this.padding.value || '${prefix}p' }} "> 
                                        {{ }else if(noPadding){ /}}
                                            <div class="${prefix}${name}-padding"> 
                                        {{ } /}}
                                                <div class="${prefix}${name}-cont"></div>
                                        {{ if(noPadding || this.padding.enable){ /}}
                                            </div>
                                            {{ } /}}
                                    </div>
                                    {{ if((!this.bullet.enable && this.footer.enable) || (this.bullet.enable && ['custom','checkboxes','select-multi'].includes(this.bullet.type) && this.footer.enable)){ /}}
                                    <div class="${prefix}${name}-foot {{ this.footer.layout !=='plain'? this.padding.value || '${prefix}p':'' }} "></div>
                                    {{ } /}}
                                </div>
                            </div>
                            `;
    }
    
    initBullets() {
        this.on('show', () => {
            this.options.autoFill.enable && this.options.autoFill.detectable && this.presetFromTarget();
        });
        if (!this.options.bullet.enable)
            return;
        if (this.targetEl) {
            isNull(this.targetEl.readOnly) ? this.targetEl.toggleAttribute('readonly', true) : this.targetEl.readOnly = true;
        }
        this.bullets.nodes = bulletTools.getChildNodes(this.contEl);
        if (this.options.autoFill.enable) {
            let vals = this.getTargetVals();
            bulletTools.setChecked(vals, this.bullets.nodes);
        }
        let childEl = getEl(this.options.autoFill.childSel, this.targetEl), fill2Hide = () => {
            if (this.targetEl) {
                this.bullets.value = this.getBulletsChecked();
                this.setTargetVals(this.bullets.value, this.targetEl, childEl);
            }
            this.hide();
        };
        for (let [i, k] of this.bullets.nodes.entries()) {
            if (this.options.bullet.type.includes('select')) {
                k.onclick = () => {
                    bulletTools.toggleChecked(k, this.bullets.nodes, this.options.bullet.type);
                    (this.options.bullet.type === 'select-single') && fill2Hide();
                };
            }
            else if (this.options.bullet.type === 'radios') {
                k.onclick = () => fill2Hide();
            }
            this.options.bullet.action && this.options.bullet.action.call(this, { el: k, data: Array.isArray(this.data) ? this.data[i] : null });
        }
    }
    presetFromTarget() {
        if (!this.options.autoFill.enable)
            return;
        let vals = this.getTargetVals();
        if (isCompField(this.options.contType)) {
            let isComp = this.data?.nodeName && this.data.nodeName.startsWith('AX-'), isEqual = vals === this.data.value;
            isComp && !isEqual && fieldTools.setVals({ target: this.data, value: vals, key: this.options.autoFill.key });
        }
        else {
            (vals !== this.bullets.value) && bulletTools.setChecked(vals, this.bullets.nodes);
        }
    }
    setBulletsChecked(vals) {
        if (!this.options.bullet.enable)
            return;
        bulletTools.setChecked(vals, this.bullets.nodes);
        if (this.options.autoFill.enable && ['radios', 'select-single'].includes(this.options.bullet.type)) {
            this.setTargetVals(this.getBulletsChecked());
        }
        this.listen({ name: 'bulletsChecked', params: [vals] });
    }
    getBulletsChecked() {
        if (!this.options.bullet.enable)
            return '';
        return bulletTools.getChecked(this.bullets.nodes);
    }
    toggleBulletsChecked(el, type = 'select-single') {
        let target = getEl(el);
        if (!target)
            return;
        bulletTools.toggleChecked(target, this.bullets.nodes, type);
    }
    clearBulletsChecked() {
        bulletTools.clearChecked(this.bullets.nodes);
    }
    checkedBulletsAll() {
        if (['radios', 'select-single'].includes(this.options.bullet.type))
            return;
        for (let k of this.bullets.nodes) {
            let input = k.querySelector('AX-CHECKBOX');
            if (input) {
                (!k.hasAttribute('disabled') && !input.hasAttribute('disabled')) && input.setAttribute('check', 'ed');
            }
            else {
                !k.hasAttribute('disabled') && k.setAttribute('checked', '');
            }
        }
    }
    setBulletsSelected(value) {
        bulletTools.setSelected(value, this.bullets.nodes);
        this.listen({ name: 'bulletsSelected', params: [value] });
    }
    getBulletsSelected() {
        return bulletTools.getSelected(this.bullets.nodes);
    }
    clearBulletsSelected() {
        bulletTools.clearSelected(this.bullets.nodes);
    }
    toggleBulletsSelected(el) {
        let target = getEl(el);
        target && bulletTools.toggleSelected(target, this.bullets.nodes);
    }
    getTargetVals() {
        if (!this.targetEl)
            return '';
        return fieldTools.getVals({ target: this.targetEl, child: getEl(this.options.autoFill.childSel, this.targetEl), key: this.options.autoFill.key });
    }
    setTargetVals(value = this.getBulletsChecked(), target = this.targetEl, child = getEl(this.options.autoFill.childSel, this.targetEl)) {
        if (!target)
            return;
        fieldTools.setVals({ target, value, child, key: this.options.autoFill.key });
        this.listen({ name: 'targetSet', params: [value] });
    }
    
    
    async reset(cb) {
        this.options = deepClone(this.dftOpts);
        await this.init();
        this.clearCache();
        this.listen && this.listen({ name: 'reset', cb });
        return this;
    }
    
    async update(settings, cb) {
        if (this.destroyed || isEmpty(settings)) {
            return this;
        }
        if (!isEmpty(settings)) {
            this.updateCache(settings);
            extend({ target: this.options, source: settings });
            if (this.state !== 'ing') {
                let tmp = this.state;
                await this.init().then((res) => {
                    tmp === 'shown' && res.show();
                });
            }
        }
        this.listen({ name: 'updated', cb, params: [settings] });
        return this;
    }
    
    async updateCont(content, cb) {
        if (this.destroyed || content === this.options.content)
            return this;
        await getContent.call(this, {
            content,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: {
                xhrName: 'contXhr',
                spinSel: this.contEl,
                ...this.options.ajax
            },
            request: (data) => {
                this.listen({ name: 'request', params: [data] });
            },
            cb: (data) => {
                this.data = this.handleData(data);
                this.renderContent(data);
                this.updateCache({ content });
                this.listen({ name: 'updatedCont', cb, params: [data] });
            }
        });
        return this;
    }
    replaceCont(content, cb) {
        if (this.destroyed)
            return this;
        let contType = getDataType(content);
        contType.includes('HTML') ? (this.contEl.innerHTML = '', this.contEl.appendChild(content)) : (this.contEl.innerHTML = content);
        this.listen({ name: 'replacedCont', cb, params: [content] });
    }
    unbindTrigger(str) {
    }
    bindTrigger(str) {
    }
    
    async transfer(el, cb) {
        if (this.destroyed)
            return this;
        let host = getEl(el);
        if (host && host !== this.targetEl) {
            let initialized = this.initialized;
            if (!initialized) {
                this.targetEl = host;
                await this.init();
            }
            else {
                this.unbindTrigger('host');
                this.targetEl = host;
                this.bindTrigger();
            }
            if (this.bubbleType === 'popup' && initialized) {
                this.positionIns.transfer(host, (target) => {
                    this.listen({ name: 'transferred', cb, params: [target] });
                });
            }
            else {
                this.listen({ name: 'transferred', cb, params: [host] });
            }
        }
        return this;
    }
    addPulseEvt() {
    }
    removePulseEvt() {
    }
    
    destroy(cb) {
        if (this.destroyed)
            return this;
        this.mainEl.remove();
        if (this.bubbleType === 'popup') {
            this.positionIns && this.positionIns.destroy();
            this.hoverIns && this.hoverIns.destroy();
            this.toggleShow && document.removeEventListener('click', this.toggleShow);
        }
        if (this.options.trigger === 'click' || this.options.trigger === 'input') {
            this.targetEl.removeEventListener(this.options.trigger, this.triggerShow);
            this.wings.forEach((i) => {
                i.removeEventListener(this.options.wing.trigger || this.options.trigger, this.triggerShow);
            });
        }
        if (this.maskEl && this.options.mask.closable) {
            this.maskEl.removeEventListener('click', this.triggerShow);
        }
        this.contEl.querySelectorAll(`[${alias}="closebubble"]`).forEach((k) => {
            k.removeEventListener('click', this.triggerClose);
        });
        if (this.bubbleType === 'dialog' && ['confirm', 'alert'].includes(this.options.feature)) {
            this.removePulseEvt();
            this.addPulseEvt();
        }
        this.canTrigger = false;
        this.contXhr && this.contXhr.abort();
        this.submitXhr && this.submitXhr.abort();
        this.destroyed = true;
        this.listen({ name: 'destroyed', cb });
        return this;
    }
}

const getAutoDur = (val) => isNull(val) ? 200 : ~~(parseFloat(val) / 3 + 250);

class Drawer extends ModBaseListenCacheBubble {
    options = {};
    parentEl;
    sizeProp;
    lastSize;
    lastPlace;
    bubbleType;
    timestamp;
    lastShowTime;
    confirmEl;
    cancelEl;
    closeEl;
    triggerShow;
    content;
    duration;
    wrapHeight;
    wrapSize;
    sequenceShow;
    sequenceHide;
    static optMaps = optDrawer;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            host: elem,
            maps: Drawer.optMaps,
            component: true,
            spread: ['mask', 'tools', 'padding', 'footer', 'autoFill', 'bullet']
        });
        
        
        
        
        
        
        
        
        
        
        
        
        this.bubbleType = 'drawer';
        this.timestamp = Date.now();
        this.lastShowTime = 0;
        let _this = this;
        this.triggerShow = function (evt) {
            if (this === _this.targetEl && _this.options.canClick) {
                let target = getEvtTarget(evt), tmp = _this.options.canClick.call(_this, target, evt);
                if (!tmp)
                    return;
            }
            if (_this.state === 'hidden') {
                _this.show();
                (_this.options.wing.actClass && _this.wings.includes(this)) && this.classList.add(_this.options.wing.actClass);
            }
            else if (_this.state === 'shown') {
                !_this.options.keepShow && _this.hide();
            }
        };
        this.triggerClose = () => {
            !this.options.keepShow && this.hide();
        };
        this.sequenceShow = (el, placement) => {
            let size = (!el.style[placement]) ? this.options.offset : (parseInt(el.style[placement]) * 0.6 + parseInt(this.options.offset) + 'px');
            el.style[placement] = size;
        };
        this.sequenceHide = (el, placement) => {
            if (el.style[placement] == this.options.offset) {
                el.style.cssText = el.style.cssText.replace(placement, '');
            }
            else {
                el.style[placement] = (parseInt(el.style[placement]) - parseInt(this.options.offset)) / 0.6 + 'px';
            }
        };
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.setEmpty();
        this.options.placement = this.options.placement || 'right';
        this.parentEl = getEl(this.options.parent) || document.body;
        this.sizeProp = (this.options.placement === 'left' || this.options.placement === 'right') ? 'width' : 'height';
        
        this.wings = getEls(this.options.wing.selector);
        this.renderBubble('drawer');
        this.setAttrs();
        await getContent.call(this, {
            content: this.options.content,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: {
                xhrName: 'contXhr',
                spinSel: this.contEl,
                ...this.options.ajax
            },
            request: (data) => {
                this.listen({ name: 'request', params: [data] });
            },
            cb: (data) => {
                this.renderContent(data);
            }
        });
        this.lastPlace = '';
        this.handleFooter();
        this.handleTools();
        this.bindTrigger();
        this.contEl.querySelectorAll(`[${alias}="closebubble"]`).forEach((k) => {
            k.removeEventListener('click', this.triggerClose);
            k.addEventListener('click', this.triggerClose, false);
        });
        this.initBullets();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    handleTools() {
        if (this.options.tools.enable) {
            this.options.tools.children.forEach((k) => {
                k.wrapEl.querySelector(`[${alias}="icon"]`);
                if (k.name === 'close') {
                    k.wrapEl.onclick = () => {
                        this.hide();
                    };
                }
                else if (['widen', 'heighten', 'enlarge'].includes(k.name)) {
                    k.wrapEl.onclick = () => {
                        if (this.lastSize === 'max') {
                            classes(k.iconEl).replace(k.icon, k.swap);
                            this.mainEl.setAttribute('size', this.options.size);
                            this.lastSize = this.options.size;
                        }
                        else {
                            classes(k.iconEl).replace(k.swap, k.icon);
                            this.mainEl.setAttribute('size', 'max');
                            this.lastSize = 'max';
                        }
                    };
                }
                else {
                    k.action && k.action.call(this, k);
                }
            });
        }
    }
    setAttrs() {
        this.options.classes && classes(this.mainEl).add(this.options.classes);
        this.options.size ? this.mainEl.setAttribute('size', this.options.size) : this.mainEl.removeAttribute('size');
        this.options.contType ? this.mainEl.setAttribute('conttype', this.options.contType) : this.mainEl.removeAttribute('conttype');
        this.contEl.toggleAttribute('dedicated', this.options.dedicated);
        this.options.placement ? this.mainEl.setAttribute('placement', this.options.placement) : this.mainEl.removeAttribute('placement', this.options.placement);
        this.options.zIndex ? addStyle(this.mainEl, 'z-index', this.options.zIndex) : removeStyle(this.mainEl, 'z-index');
        if (this.parentEl !== document.body) {
            let pStyle = style(this.parentEl);
            pStyle.position === 'static' && addStyle(this.parentEl, 'position', 'relative');
            pStyle.overflow !== 'hidden' && addStyle(this.parentEl, 'overflow', 'hidden');
            this.mainEl.toggleAttribute('restrict', true);
        }
        else {
            this.mainEl.removeAttribute('restrict');
        }
    }
    getBeforeItems() {
        return instance.data.filter((k) => k.ins !== this &&
            k.type === 'drawer' &&
            !k.ins.destroyed &&
            k.ins.state === 'shown' &&
            k.ins.parentEl === this.parentEl &&
            k.ins.options.placement === this.options.placement &&
            k.ins.lastShowTime < this.lastShowTime);
    }
    getIngItems() {
        return instance.data.filter((k) => k.ins !== this &&
            k.type === 'drawer' &&
            !k.ins.destroyed &&
            k.ins.state === 'ing' &&
            k.ins.parentEl === this.parentEl &&
            k.ins.options.placement === this.options.placement);
    }
    bindTrigger() {
        if (this.options.trigger === 'click') {
            if (this.targetEl && !this.canTrigger) {
                this.targetEl.removeEventListener('click', this.triggerShow);
                this.targetEl.addEventListener('click', this.triggerShow, false);
            }
            if (this.wings.length > 0 && !this.canTrigger) {
                this.wings.forEach((k) => {
                    k.removeEventListener('click', this.triggerShow);
                    k.addEventListener('click', this.triggerShow, false);
                });
            }
        }
        else if (this.options.trigger === 'sticky') {
            this.show();
        }
        if (this.maskEl && this.options.mask.closable) {
            this.maskEl.removeEventListener('click', this.triggerShow);
            this.maskEl.addEventListener('click', this.triggerShow, false);
        }
        this.canTrigger = true;
    }
    unbindTrigger(mode = 'host') {
        this.canTrigger = false;
        if (this.options.trigger === 'click') {
            this.targetEl.removeEventListener('click', this.triggerShow);
            if (this.wings.length > 0 && mode === 'all') {
                this.wings.forEach((item) => {
                    item.removeEventListener('click', this.triggerShow);
                });
            }
        }
        if (this.maskEl && this.options.mask.closable && mode === 'all') {
            this.maskEl.removeEventListener('click', this.triggerShow);
        }
    }
    
    async show(cb) {
        if (this.destroyed || this.state !== 'hidden' || this.getIngItems().length > 0 || this.options.asleep)
            return;
        this.state = 'ing';
        this.options.b4Show && await this.options.b4Show.call(this);
        elState(this.mainEl).isVirtual && this.parentEl.appendChild(this.mainEl);
        super.listen({ name: 'show', cb });
        this.wrapSize = parseInt(style(this.wrapEl)[this.sizeProp]);
        this.duration = this.options.duration || (this.options.autoDur ? getAutoDur(this.wrapHeight) : parseFloat(style(this.wrapEl).animationDuration) * 1000);
        this.wrapEl.style.transitionDuration = `${this.duration}ms`;
        this.targetEl && this.targetEl.classList.add(this.options.actClass);
        this.lastShowTime = Date.now();
        this.getBeforeItems().forEach((k) => {
            this.sequenceShow(k.ins.wrapEl, this.options.placement);
        });
        this.mainEl.setAttribute('show', '');
        await delay({
            duration: this.duration,
            done: () => {
                this.state = 'shown';
                super.listen({ name: 'shown', cb });
            }
        });
        return this;
    }
    
    async hide(cb) {
        if (this.destroyed || this.state !== 'shown' || this.getIngItems().length > 0) {
            return this;
        }
        this.state = 'ing';
        if (!this.options.deadShow) {
            this.options.b4Hide && await this.options.b4Hide.call(this);
            super.listen({ name: 'hide', cb });
            this.targetEl ? this.targetEl.classList.remove(this.options.actClass) : null;
            this.options.wing.actClass && this.wings.map((k) => { k.classList.remove(this.options.wing.actClass); });
            this.maskEl && (this.maskEl.style.opacity = 0);
            this.wrapEl.style[this.options.placement] = '-' + style(this.wrapEl)[this.sizeProp];
            this.getBeforeItems().forEach((k) => {
                this.sequenceHide(k.ins.wrapEl, this.options.placement);
            });
            this.mainEl.querySelectorAll('video,audio').forEach((k) => {
                k.pause();
            });
            await delay({
                duration: this.duration,
                done: () => {
                    this.state = 'hidden';
                    this.mainEl.removeAttribute('show');
                    this.maskEl ? this.maskEl.removeAttribute("style") : null;
                    this.wrapEl.removeAttribute("style");
                    this.mainEl.remove();
                    super.listen({ name: 'hidden', cb });
                }
            });
        }
        return this;
    }
}

const privacy = ({ content = config.lang.privacy.content, contType, contData, cb }) => {
    if (!storage.get('AXUI_GOT_PRIVACY')) {
        new Drawer(undefined, {
            content,
            contType,
            contData,
            footer: {
                children: [
                    {
                        name: 'canceled',
                        label: config.lang.privacy.cancel,
                    },
                    {
                        name: 'confirmed',
                        label: config.lang.privacy.confirm,
                    },
                ]
            },
            onConfirmed: () => {
                storage.set('AXUI_GOT_PRIVACY', true);
                cb && cb();
            }
        }).show();
    }
};

const support = (useCont = false, cb) => {
    try {
        createEl('div').querySelector(':has');
    }
    catch (e) {
        if (useCont && !storage.get('AXUI_GOT_SUPPORT')) {
            new Drawer(undefined, {
                content: config.lang.support.content,
                footer: {
                    children: [
                        {
                            name: 'canceled',
                            label: config.lang.support.cancel,
                        },
                        {
                            name: 'confirmed',
                            label: config.lang.support.confirm,
                        },
                    ]
                },
                onConfirmed: () => {
                    storage.set('AXUI_GOT_SUPPORT', true);
                    cb && cb();
                }
            }).show();
        }
        return false;
    }
    return true;
};

const getSelectorType = (str) => {
    requireTypes(str, 'string');
    str = str.trim();
    let type = '', isUpperCase = (letter) => (letter >= 'A' && letter <= 'Z') ? true : false;
    if (str) {
        if (str.includes(' ')) {
            type = 'nesting';
        }
        else {
            type = (str.startsWith('#') ? 'id' :
                str.startsWith('.') ? 'class' :
                    (str.startsWith('[') && str.endsWith(']')) ? 'attr' :
                        ([...str].every(i => isUpperCase(i))) ? 'node' : '');
        }
    }
    return type;
};

const purifyHtml = (str) => trim(str.replace(/<[^>]+>/g, ''));

const offset = (elem) => {
    let totalLeft, totalTop, scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft, scrollTop = document.documentElement.scrollTop || document.body.scrollTop, targetEl = getEl(elem);
    if (!targetEl) {
        throw new Error(`The target node does not exist!`);
    }
    totalLeft = targetEl.getBoundingClientRect().left + scrollLeft;
    totalTop = targetEl.getBoundingClientRect().top + scrollTop;
    return {
        left: totalLeft,
        top: totalTop
    };
};

const contains = (child, parent) => {
    let childEl = getEl(child), result = false;
    if (!childEl)
        return result;
    if (Array.isArray(parent)) {
        for (let k of parent) {
            let parentEl = getEl(k);
            if (!parentEl)
                continue;
            if (parentEl.contains(childEl)) {
                result = true;
                break;
            }
        }
    }
    else {
        let parentEl = getEl(parent);
        result = parentEl && parentEl.contains(childEl) ? true : false;
    }
    return result;
};

const tplToEls = (tpl) => {
    tpl = tpl.trim().replaceAll('&lt;', '<').replaceAll('&gt;', '>');
    if (!tpl)
        return [];
    let tplNode = createEl('TEMPLATE', '', tpl), clone = tplNode.content.cloneNode(true), firstEl = clone.firstElementChild, result;
    if (!firstEl)
        return [];
    if (firstEl.nodeName === 'TR') {
        let trs = [...clone.querySelectorAll('TR')], tmp = tplNode.innerHTML.split('</tr>').map(k => k.replace(/<\/?tr[^>]*>/g, ''));
        tmp.shift();
        trs.forEach((k, i) => {
            k.innerHTML = tmp[i];
        });
        result = trs;
    }
    else {
        result = [...clone.children];
    }
    return result;
};

const getHeights = (elem) => {
    let target = getEl(elem);
    if (!target) {
        throw new Error('The target node does not exist!');
    }
    let styles = style(target);
    return {
        height: parseInt(styles.height),
        paddingTop: parseInt(styles.paddingTop),
        paddingBottom: parseInt(styles.paddingBottom),
        marginTop: parseInt(styles.marginTop),
        marginBottom: parseInt(styles.marginBottom),
        borderTopWidth: parseInt(styles.borderTopWidth),
        borderBottomWidth: parseInt(styles.borderBottomWidth),
    };
};

const getWidths = (elem) => {
    let target = getEl(elem);
    if (!target) {
        throw new Error('The target node does not exist!');
    }
    let styles = style(target);
    return {
        width: parseInt(styles.width),
        paddingLeft: parseInt(styles.paddingLeft),
        paddingRight: parseInt(styles.paddingRight),
        marginLeft: parseInt(styles.marginLeft),
        marginRight: parseInt(styles.marginRight),
        borderLeftWidth: parseInt(styles.borderLeftWidth),
        borderRightWidth: parseInt(styles.borderRightWidth),
    };
};

const getElSpace = (target, axis = 'y') => {
    let el = getEl(target), tmp;
    if (!el)
        return 0;
    if (axis === 'x') {
        tmp = getWidths(el);
        return el.offsetWidth + tmp.marginLeft + tmp.marginRight;
    }
    else {
        tmp = getHeights(el);
        return el.offsetHeight + tmp.marginTop + tmp.marginBottom;
    }
};

const curveFns = {
    
    linear: (t) => t,
    
    ease: (t) => ((1 - t) ** 2) * t * 3 * 0.3 + (t ** 2) * (1 - t) * 3 * 1 + t ** 3,
    
    easeIn: (t) => (t ** 2) * (1 - t) * 3 * 0.2 + t ** 3,
    
    easeOut: (t) => ((1 - t) ** 2) * t * 3 * 0.8 + (t ** 2) * (1 - t) * 3 * 1 + t ** 3,
    
    easeInOut: (t) => t < 0.5 ? Math.sqrt(0.5 * t) : -Math.sqrt(0.5 * (-t + 1)) + 1,
    
    easeOutIn: (t) => t < 0.5 ? 2 * t ** 2 : -2 * (t - 1) ** 2 + 1,
};

let ease = ({ from, to, before, doing, done, progress, duration, curve }) => {
    
    if (!from || !to)
        throw new Error('from and to are required!');
    let initTime = Date.now(), result = { step: 0, value: { ...from }, frame: 0, remaining: 0 }, repeat = () => {
        let dftTime = duration || 500, newTime = Date.now() - initTime, ratio = newTime / dftTime, step = curveFns[curve || 'easeOut'](ratio), value = { ...from }, remaining = dftTime - newTime, frame = requestAnimationFrame(repeat);
        for (let k in from) {
            from.hasOwnProperty(k) && from[k] !== to[k] ? value[k] = from[k] + (to[k] - from[k]) * step : null;
        }
        result = { step, frame, value, remaining };
        ax.frame = frame;
        if (ratio < 1) {
            progress && progress(result);
            doing && doing(result);
        }
        else {
            cancelAnimationFrame(frame);
            result.step = 1;
            result.value = { ...to };
            result.remaining = 0;
            progress && progress(result);
            done && done(result);
        }
    };
    before && before(result);
    repeat();
    return result;
};

let slideDown = ({ el, display = 'block', before, doing, done, duration, curve = 'easeOut', cut = true }) => {
    
    let target = getEl(el);
    if (!target) {
        throw new Error('The target node does not exist!');
    }
    let state = elState(target);
    if (state.isHidden) {
        let rawStyle = target.style.cssText, dftCss = `${rawStyle}display: ${display};`, startCss = `${dftCss}${cut ? 'overflow:hidden;' : ''}`;
        target.style.cssText = startCss;
        let { height, paddingTop, paddingBottom, marginTop, marginBottom } = getHeights(target), time = ~~duration && duration !== 0 ? ~~duration : getAutoDur(height);
        target.style.cssText += `padding-top: 0; padding-bottom: 0;margin-top: 0; margin-bottom: 0;`;
        ease({
            from: { height: 0, paddingTop: 0, paddingBottom: 0, marginTop: 0, marginBottom: 0 },
            to: { height, paddingTop, paddingBottom, marginTop, marginBottom },
            duration: time,
            curve,
            before: (e) => {
                before && before.call(target, { frame: e.frame, value: e.value.height, step: e.step, for: 'show' });
            },
            doing: (e) => {
                target.style.cssText = `
                ${startCss}
                padding-top: ${e.value.paddingTop}px; 
                padding-bottom: ${e.value.paddingBottom}px;
                margin-top: ${e.value.marginTop}px; 
                margin-bottom: ${e.value.marginBottom}px; 
                height: ${e.value.height}px
                `;
                doing && doing.call(target, { frame: e.frame, value: e.value.height, step: e.step, target, for: 'show' });
            }, done: (e) => {
                target.style.cssText = dftCss;
                done && done.call(target, { frame: e.frame, value: e.value.height, step: e.step, target, for: 'show' });
            }
        });
    }
    else if (state.isVirtual) {
        target.style.display = display;
        done && done.call(target);
    }
    return target;
};

let slideUp = ({ el, display = 'block', before, doing, done, duration, curve = 'easeOut', cut = true }) => {
    let target = getEl(el);
    if (!target) {
        throw new Error('The target node does not exist!');
    }
    let state = elState(target);
    if (state.isCalc) {
        let rawStyle = target.style.cssText, { height, paddingTop, paddingBottom, marginTop, marginBottom } = getHeights(target), time = ~~duration && duration !== 0 ? ~~duration : getAutoDur(height), dftCss = `${rawStyle}display: ${display};`, startCss = `${dftCss}${cut ? 'overflow:hidden;' : ''}`;
        target.style.cssText = `${startCss}padding-top: 0; padding-bottom: 0;margin-top: 0; margin-bottom: 0;`;
        ease({
            from: { height, paddingTop, paddingBottom, marginTop, marginBottom },
            to: { height: 0, paddingTop: 0, paddingBottom: 0, marginTop: 0, marginBottom: 0 },
            duration: time,
            curve,
            before: (e) => {
                before && before.call(target, { frame: e.frame, value: e.value.height, step: e.step, for: 'hide' });
            },
            doing: (e) => {
                target.style.cssText = `
                ${startCss}
                padding-top: ${e.value.paddingTop}px; 
                padding-bottom: ${e.value.paddingBottom}px;
                margin-top: ${e.value.marginTop}px; 
                margin-bottom: ${e.value.marginBottom}px; 
                height: ${e.value.height}px
                `;
                doing && doing.call(target, { frame: e.frame, value: e.value.height, step: e.step, for: 'hide' });
            }, done: (e) => {
                target.style.cssText = `${rawStyle}display: none`;
                done && done.call(target, { frame: e.frame, value: e.value.height, step: e.step, for: 'hide' });
            }
        });
    }
    else if (state.isVirtual) {
        target.style.display = 'none';
        done && done.call(target);
    }
    return target;
};

let slideToggle = ({ el, display = 'block', before, doing, done, duration, curve = 'easeOut', cut = true }) => {
    let target = getEl(el);
    if (!target)
        throw new Error('The target node does not exist!');
    if (elState(target).isHidden) {
        slideDown({ el: target, display, duration, curve, cut, before, doing: doing?.down, done: done?.down });
    }
    else if (elState(target).isCalc) {
        slideUp({ el: target, display, duration, curve, cut, before, doing: doing?.up, done: done?.up });
    }
    return target;
};

let easeHeight = ({ el, height, type = 'down', doing, done, duration, curve = 'easeOut', cut = true }) => {
    let target = getEl(el);
    if (!target) {
        throw new Error('The target node does not exist!');
    }
    if (elState(target).isCalc) {
        let size = getHeights(target).height, _height = ~~height && height !== 0 ? ~~height : size, time = ~~duration && duration !== 0 ? ~~duration : getAutoDur(_height), dftCss = `${target.style.cssText} ${cut ? 'overflow:hidden;' : ''};`, from = {}, to = {};
        target.style.cssText = `${dftCss}`;
        if (type === 'to' && ~~height) {
            from = { height: size };
            to = { height };
        }
        else if (type === 'up') {
            from = { height: _height };
            to = { height: 0 };
        }
        else {
            from = { height: 0 };
            to = { height: _height };
        }
        ease({
            from,
            to,
            duration: time,
            curve,
            doing: (e) => {
                target.style.cssText = `
                ${dftCss}
                height: ${e.value.height}px
                `;
                doing && doing.call(target, { frame: e.frame, value: e.value.height, step: e.step });
            }, done: (e) => {
                done && done.call(target, { frame: e.frame, value: e.value.height, step: e.step });
            }
        });
    }
    return target;
};

let fadeIn = ({ el, display = 'block', doing, done, duration = 200, curve = 'easeOut' }) => {
    let target = getEl(el);
    if (!target) {
        throw new Error('The target node does not exist!');
    }
    let state = elState(target), opacity = parseFloat(style(target).opacity), from = { opacity: 0 };
    if (opacity === 1) {
        return target;
    }
    if (state.isHidden) {
        from.opacity = 0;
        target.style.display = display;
        target.style.opacity = 0;
    }
    else if (state.isCalc && opacity !== 1) {
        from.opacity = opacity;
    }
    ease({
        from,
        to: { opacity: 1 },
        duration,
        curve,
        doing: (e) => {
            target.style.opacity = e.value.opacity;
            doing && doing.call(target, { frame: e.frame, value: e.value.opacity, step: e.step, target, for: 'show' });
        }, done: (e) => {
            target.style.opacity = 1;
            done && done.call(target, { frame: e.frame, value: e.value.opacity, step: e.step, target, for: 'show' });
        }
    });
    return target;
};

let fadeOut = ({ el, display, doing, done, duration = 200, curve = 'easeOut' }) => {
    let target = getEl(el);
    if (!target) {
        throw new Error('The target node does not exist!');
    }
    let opacity = parseFloat(style(target).opacity);
    if (opacity === 0) {
        return target;
    }
    display ? target.style.display = display : null;
    if (elState(target).isCalc) {
        ease({
            from: { opacity },
            to: { opacity: 0 },
            duration,
            curve,
            doing: (e) => {
                target.style.opacity = e.value.opacity;
                doing && doing.call(target, { frame: e.frame, value: e.value.opacity, step: e.step, target, for: 'hide' });
            }, done: (e) => {
                target.style.opacity = 0;
                done && done.call(target, { frame: e.frame, value: e.value.opacity, step: e.step, target, for: 'hide' });
            }
        });
    }
    return target;
};

let fadeToggle = ({ el, display = 'block', doing, done, duration = 200, curve = 'easeOut' }) => {
    let target = getEl(el);
    if (!target)
        throw new Error('The target node does not exist!');
    let state = elState(target), opacity = parseFloat(style(target).opacity);
    if (state.isHidden) {
        fadeIn({ el: target, display, duration, curve, doing: doing?.in, done: done?.in });
    }
    else if (state.isCalc) {
        if (opacity <= 0.5) {
            fadeIn({ el: target, display, duration, curve, doing: doing?.in, done: done?.in });
        }
        else {
            fadeOut({ el: target, display, duration, curve, doing: doing?.out, done: done?.out });
        }
    }
    return target;
};

let show = ({ el, display = 'block', done }) => {
    let target = getEl(el);
    if (!target) {
        throw new Error('The target node does not exist!');
    }
    if (display == 'visible') {
        target.style.visibility = 'visible';
    }
    else {
        target.style.display = display;
    }
    done && done.call(target, { target, for: 'show' });
    return target;
};

let hide = ({ el, display = 'none', done }) => {
    let target = getEl(el);
    if (!target) {
        throw new Error('The target node does not exist!');
    }
    if (display === 'hidden') {
        target.style.visibility = 'hidden';
    }
    else {
        target.style.display = 'none';
    }
    done && done.call(target, { target, for: 'hide' });
    return target;
};

let toggle = ({ el, display = { show: 'block', hide: 'none' }, done }) => {
    let target = getEl(el);
    if (!target)
        throw new Error('The target node does not exist!');
    elState(target).isVisible ? hide({ el: target, display: display.hide, done: done?.hide }) : show({ el: target, display: display.show, done: done?.show });
    return target;
};

const pipe = (...args) => {
    args = args.filter(Boolean);
    return (initial) => args.reduce((prev, cur) => cur(prev), initial);
};

const regExps = {
    email: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/,
    ip: /^\d+\.\d+\.\d+\.\d+$/,
    url: /^(?=^.{3,255}$)(http(s)?:\/\/)?(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+\.\w+)*$/,
    locale: /^[\u0391-\uFFE5]+$/,
    letter: /^[a-zA-Z]+$/,
    string: /^[a-zA-Z0-9]+$/,
    password: `^[a-zA-Z0-9${ax.regChars}]+$`,
    ymdhms: /^(\d{4})(-|\/)(\d{1,2})\2(\d{1,2}) (\d{1,2}):(\d{1,2}):(\d{1,2})$/,
    ymd: /^(\d{4})(-|\/)(\d{1,2})\2(\d{1,2})$/,
    hms: /^((20|21|22|23|[0-1]\d)\:[0-5][0-9])(\:[0-5][0-9])?$/,
    ym: /^(\d{4})(-|\/)(\d{1,2})$/,
    y: /^(\d{4})$/,
    m: /^(0?[1-9]|1[0-2])$/,
    d: /^((0?[1-9])|((1|2)[0-9])|30|31)$/,
    integer: /^[1-9]\d*$/,
    number: /^(-?\d+)\.?(\d*)$/,
    cellphone: /^[1][3456789][0-9]{9}$/,
    landline: /^([0-9]{3,4}-)?[0-9]{7,8}$/,
    plate: {
        nev: /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))/,
        oil: /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1}$/,
    },
    id: /^(([1][1-5])|([2][1-3])|([3][1-7])|([4][1-6])|([5][0-4])|([6][1-5])|([7][1])|([8][1-2]))\d{4}(([1][9]\d{2})|([2]\d{3}))(([0][1-9])|([1][0-2]))(([0][1-9])|([1-2][0-9])|([3][0-1]))\d{3}[0-9xX]$/,
    zip: /^\d{6}$/,
};

const replaceFrag = ({ origin, text, start, stop, length }) => {
    typeof origin !== 'string' ? origin = JSON.stringify(origin) : null;
    typeof text !== 'string' ? text = JSON.stringify(text) : null;
    let result = '';
    if (start === undefined && stop === undefined) {
        result = text;
    }
    else if (start !== undefined && stop === undefined) {
        result = length !== undefined ? origin.substring(0, start) + text + origin.substring(start + length) : origin.substring(0, start) + text;
    }
    else if (start === undefined && stop !== undefined) {
        result = length !== undefined ? origin.substring(stop - length) + text + origin.substring(0, start) : text + origin.substring(stop);
    }
    else {
        result = origin.substring(0, start) + text + origin.substring(stop);
    }
    return result;
};

const paramToJson = (str) => {
    let result;
    if (str) {
        str = str.trim();
        let objStr = sliceFrags({ str }).join(''), arrStr = sliceFrags({ str, start: '[', end: ']' }).join(''), excludeStr = objStr + arrStr, std = str.split(',').filter(Boolean).map((k) => {
            return (!k.includes(':') && !excludeStr.includes(k)) ? `${k}:true` : k;
        }).join(',');
        try {
            result = parseStr({
                content: `{${std}}`,
                type: 'object',
                catchable: true,
            });
        }
        catch {
            result = {};
            console.warn('Data parsing error, an empty object "{}" has been returned!');
        }
    }
    return result;
};

const getLast = (data) => {
    let dataType = getDataType(data), last = null;
    if (dataType === 'Array' && data.length > 0) {
        last = data.slice(-1)[0];
    }
    else if (dataType === 'Object' && Object.keys(data).length > 0) {
        last = Object.keys(data).slice(-1)[0];
    }
    return last;
};

const isSubset = (child, parent) => (Array.isArray(child)) ? child.length <= parent.length && child.every(k => parent.includes(k)) : parent.includes(child);

const stdParam = (val, dft = {}, prop) => {
    let result = (getDataType(dft) === 'Object') ? { ...dft } : {};
    if (getDataType(val) === 'Object') {
        extend({
            target: result,
            source: val,
        });
    }
    else {
        if (prop && !isNull(val)) {
            Reflect.set(result, prop, val);
        }
    }
    return result;
};

const throttle = (fn, options) => {
    let dft = { intvl: config.throttle, prevent: false }, opts = stdParam(options, dft, 'intvl');
    let last = 0, timer = null, handler = function (e) {
        let now = Date.now();
        if (now - last > opts.intvl) {
            if (timer) {
                clearTimeout(timer);
                timer = null;
            }
            fn.apply(this, arguments);
            last = now;
        }
        else {
            opts.prevent ? (e.stopPropagation(), e.preventDefault()) : null;
        }
    };
    handler.cancel = () => {
        clearTimeout(timer);
        timer = null;
        return handler;
    };
    return handler;
};

const debounce = (fn, options) => {
    let dft = { delay: config.debounce, prevent: false, auto: false }, opts = stdParam(options, dft, 'delay');
    if (isMobi && opts.auto) {
        return throttle(fn, { intvl: opts.delay, prevent: opts.prevent });
    }
    else {
        let timer = null, handler = function (e) {
            opts.prevent ? (e.stopPropagation(), e.preventDefault()) : null;
            timer ? clearTimeout(timer) : null;
            opts.instant && opts.instant();
            timer = setTimeout(() => {
                fn.apply(this, arguments);
            }, opts.delay);
        };
        handler.cancel = () => {
            timer && clearTimeout(timer);
            return handler;
        };
        return handler;
    }
};

const convertByte = ({ val, to = '', places = 2, outer = 'span', inner = 'i', units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] }) => {
    val = clampVal({ val, min: 0, max: '' });
    let lvs = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'], unit = '', lv = 0, base = val / 1024, getVal = (i) => i === 0 ? val : toNumber(val / (1024 ** i), { places });
    if (lvs.includes(to)) {
        lv = lvs.findIndex(k => k === to);
    }
    else {
        lv = (base >= 1024 ** 4) ? 5 :
            (base >= 1024 ** 3) ? 4 :
                (base >= 1024 ** 2) ? 3 :
                    (base >= 1024) ? 2 :
                        (base >= 1) ? 1 : 0;
    }
    val = getVal(lv);
    unit = units[lv];
    return { val, unit, str: `<${outer}><${inner}>${val}</${inner}>${unit}</${outer}>` };
};

const fileTools = {
    download: (url, name) => {
        let xhr = new XMLHttpRequest();
        xhr.open("GET", url, true);
        xhr.responseType = 'blob';
        xhr.onload = function () {
            let url = window.URL.createObjectURL(xhr.response), dom = createEl('a');
            dom.src = url || 'javascript:void(0);';
            dom.download = name || config.lang.placeholder.downloadName + Date.now();
            dom.click();
        };
        xhr.send();
    },
    getBase64: (file, cb) => {
        return new Promise((resolve, reject) => {
            try {
                let reader = new FileReader();
                reader.readAsDataURL(file);
                reader.onload = (e) => {
                    cb && cb(e?.target?.result);
                    resolve(e?.target?.result);
                };
            }
            catch (e) {
                reject(e);
            }
        });
    },
    getSuffix: (file) => {
        if (file.name.includes('.')) {
            return file.name.split('.').pop();
        }
        else {
            return '';
        }
    },
    getFileName: (url) => {
        try {
            return url.split('/').pop()?.trim() || '';
        }
        catch {
            return '';
        }
    },
    urlToFile: function (url, name = '', cb) {
        name = name || this.getFileName(url) || config.lang.placeholder.fileName + Date.now();
        return new Promise((resolve, reject) => {
            ajax({
                url,
                respType: 'blob',
                type: 'get',
                error: (res) => {
                    reject(res.content);
                },
                success: (res) => {
                    let file = new File([res.content], name, { type: res.content.type });
                    cb && cb(file);
                    resolve(file);
                }
            });
        });
    }
};

const increaseId = (data, hasId = true) => {
    let ids = new Set(), newId = 0;
    if (!data.length)
        return newId;
    if (hasId) {
        for (let k of data)
            ids.add(~~k.id);
        newId = Math.max(...ids) + 1;
    }
    else {
        newId = data.length;
    }
    return newId;
};

const findItem = (val, source, key, prio) => {
    let valType = getDataType(val);
    if (!source)
        return val;
    let result, first = source[0], firstId = first?.id, idType = getDataType(firstId);
    if (isNull(firstId) && valType === 'Number') {
        result = source[val];
    }
    else {
        if (key) {
            result = source.find((k) => k[key] === val);
        }
        else {
            if (valType.includes('HTML')) {
                let hasPrio = (item) => prio?.node ? item[prio.node] === val : false, condition = (item) => hasPrio(item) || item.headEl === val || item.wrapEl === val;
                result = source.find((k) => condition(k));
            }
            else {
                let prop = (valType === 'Number') ? (prio?.number || 'id') :
                    (valType === 'String') ? prio?.string || (idType === 'String' ? 'id' : 'label') : '';
                if (valType === 'Object') {
                    result = source.find((k) => k === val);
                }
                else {
                    result = prop ? source.find((k) => k[prop] === val || k[prio?.guard || 'value'] === val) : undefined;
                }
            }
        }
    }
    return result;
};

const splice = ({ host = [], source, intent = 'end+', index = 0, cb }) => {
    if (isNull(host) || !Array.isArray(host))
        return [];
    let tmp = source !== undefined ? (Array.isArray(source) ? source : [source]) : null, data = Array.isArray(tmp) ? tmp : [tmp], idx = isNull(index) ? 0 : clampVal({ min: 0, max: host.length - 1, val: index }), items = [];
    if (intent === 'clear') {
        items = [...host];
        host.splice(0);
    }
    else if (intent === 'add') {
        if (!tmp)
            return host;
        let start = (idx >= host.length) ? (host.length - 1) : idx;
        host.splice(start, 0, ...data);
        items = host.slice(start, start + data.length);
    }
    else if (intent === 'remove') {
        let _tmp = source !== undefined ? host.indexOf(source) : idx;
        if (_tmp < 0) {
            return host;
        }
        else {
            items = host.slice(_tmp, _tmp + 1);
            host.splice(_tmp, 1);
        }
    }
    else if (intent === 'replace') {
        if (!tmp)
            return host;
        host.splice(idx, 1, ...data);
        items = host.slice(idx, idx + data.length);
    }
    else if (intent === 'start-') {
        items = host.slice(0, 1);
        host.shift();
    }
    else if (intent === 'end-') {
        items = host.slice(-1);
        host.pop();
    }
    else if (intent === 'start+') {
        if (!tmp)
            return host;
        host.unshift(...data);
        items = host.slice(0, data.length);
    }
    else {
        if (!tmp)
            return host;
        host.push(...data);
        items = host.slice(-data.length);
    }
    cb && cb(items);
    return host;
};

const dl2Tree = (el) => {
    let elem = getEl(el), result = [];
    if (!elem) {
        console.warn(`No node, no access to data!`);
        return result;
    }
    let heads = [...elem.querySelectorAll('dt')], bodys = [...elem.querySelectorAll('dd')];
    if (heads.length !== bodys.length) {
        console.warn('Inconsistent number of "dt" and "dd" tags!');
        return result;
    }
    if (heads.length === 0) {
        console.warn('No "dt" and "dd" tags!');
        return result;
    }
    result = heads.map((k, i) => {
        return { content: bodys[i].innerHTML, ...getValsFromAttrs(k) };
    });
    return result;
};

const treeTools = {
    
    find: function (arr, val, key) {
        let tmp = findItem(arr, val, key);
        if (tmp)
            return tmp;
        for (let k of arr) {
            k.children && this.find(k.children, val, key);
        }
        return null;
    },
    getBoolItems: (data, prop, negative = false) => {
        return data.filter((k) => negative ? !k[prop] : k[prop]);
    },
    
    toFlat: function (data) {
        let result = [];
        data.forEach(k => {
            if (k.children && !data.includes(k.children[0])) {
                result = [...result, k, ...this.toFlat(k.children)];
            }
            else {
                result.push(k);
            }
        });
        return result;
    },
    
    toTree: (data) => {
        if (!data || data.length === 0 || !data[0].hasOwnProperty('pId')) {
            return data;
        }
        let root = data[0].pId;
        data.forEach(k => {
            let children = data.filter(i => k.id == i.pId);
            if (children.length) {
                k.children = children;
            }
        });
        return data.filter(k => k.pId == root);
    },
    
    addIdPath: ({ source, rootStart = config.rootStart, idStart = config.idStart, floorStart = config.floorStart, pathHyphen = config.pathHyphen, lbl2Val = true }) => {
        if (!Array.isArray(source) || source.length === 0 || source[0].hasOwnProperty('path')) {
            return source;
        }
        let hasId = source[0].hasOwnProperty('id') ? true : false, index = idStart, each = (data, floor, path = rootStart) => {
            data.forEach(item => {
                if (!hasId) {
                    item.id = index;
                    index++;
                }
                item.floor = floor;
                item.pId = item.pId || item.pId === 0 ? item.pId : rootStart;
                item.path = path + pathHyphen + item.id;
                (!item.hasOwnProperty('value') && item.hasOwnProperty('label') && lbl2Val) ? item.value = item.label : null;
                if (item.children && item.children.length > 0) {
                    item.children.forEach((k) => {
                        k.pId = item.id;
                    });
                    each(item.children, floor + 1, item.path);
                }
                if (!item.path) {
                    item.path = item.pId + pathHyphen + item.id;
                }
            });
        };
        each(source, floorStart);
        return source;
    },
    fromInput: (el, hyphen = config.splitHyphen) => {
        let elem = getEl(el), result = [];
        if (!elem) {
            console.warn(`No node, no access to data!`);
            return result;
        }
        let tmp = elem.value.trim();
        return !tmp ? [] : elem.value.trim().split(hyphen).map((k) => { return { label: k, value: k }; });
    },
    
    createBranchObj: function ({ source, flatData, target, isChild = true, isLeaf = true, rootStart = config.rootStart, idStart = config.idStart, floorStart = config.floorStart, pathHyphen = config.pathHyphen }) {
        let obj = {}, targetObj = findItem(target, flatData), emptyArr = flatData.length === 0, sourceType = getDataType(source), setIdLabelVal = (data, id) => {
            !data.hasOwnProperty('id') ? data.id = id : null;
            !data.hasOwnProperty('value') ? (data.hasOwnProperty('label') ? data.value = data.label : data.value = '') : null;
            !data.hasOwnProperty('label') ? data.label = config.lang.tree.label + id : null;
        }, setRootChild = (data, id) => {
            let base = {
                pId: rootStart,
                path: rootStart + pathHyphen + id,
                floor: floorStart
            };
            setIdLabelVal(data, id);
            Object.assign(data, base);
        };
        if (isNull(flatData)) {
            return obj;
        }
        if (sourceType === 'String' || sourceType === 'Number') {
            obj.value = obj.label = source;
        }
        else if (sourceType === 'Object') {
            obj = { ...source };
        }
        if (emptyArr) {
            setRootChild(obj, idStart);
        }
        else {
            let newId = increaseId(flatData);
            if (!targetObj) {
                obj.hasOwnProperty('id') ? newId = obj.id : null;
                setRootChild(obj, newId);
            }
            else {
                !targetObj.hasOwnProperty('children') && isChild && (targetObj.children = []);
                let props = {};
                setIdLabelVal(obj, newId);
                props = isChild ? {
                    pId: targetObj.id,
                    path: targetObj.path + pathHyphen + obj.id,
                    floor: targetObj.floor + 1
                } : {
                    pId: targetObj.pId,
                    path: targetObj.path.replace(new RegExp('(.*)' + targetObj.id), '$1' + obj.id),
                    floor: targetObj.floor
                };
                Object.assign(obj, props);
            }
        }
        !isLeaf && (obj.children = []);
        return obj;
    },
    
    childToParent: (source, arrowEnd = true) => {
        if (!source.children) {
            return;
        }
        if (!source.childrenEl) {
            source.childrenEl = createEl('ul');
            source.headEl.insertAdjacentElement('afterEnd', source.childrenEl);
        }
        if (!source.arrowEl) {
            source.arrowEl = createEl('i', { [alias]: 'arrow' });
            source.headEl.insertAdjacentElement(arrowEnd ? 'beforeEnd' : 'afterBegin', source.arrowEl);
        }
    },
    parentToChild: (source) => {
        if (source.children) {
            return;
        }
        source.target.childrenEl.remove();
        source.target.childrenEl = null;
        source.target.arrowEl.remove();
        source.target.arrowEl = null;
        source.target.expanded = false;
    },
    
    addBranch: function ({ source, rootEl, flatData, treeData, brother, isFront = true, repeat = true, autoFill = true, cb }) {
        if (!flatData || !source) {
            return;
        }
        let existing = flatData.find((k) => k.pId === source.pId && k.label === source.label), brotherObj = findItem(brother, flatData), parentObj = findItem(source.pId, flatData), result;
        if (!repeat && existing) {
            console.warn(`A branch with the same label (${source.label}) and pId (${source.pId}) already exists. Duplicate addition is not allowed!`);
            return;
        }
        if (parentObj) {
            if (brotherObj && parentObj.children.includes(brotherObj)) {
                let index = flatData.indexOf(brotherObj);
                if (isFront) {
                    splice({ host: parentObj.children, source, index, intent: 'add', cb: (resp) => result = resp[0] });
                    autoFill && brotherObj.wrapEl.insertAdjacentElement('beforeBegin', source.wrapEl);
                }
                else {
                    splice({ host: parentObj.children, source, index: index + 1, intent: 'add', cb: (resp) => result = resp[0] });
                    autoFill && brotherObj.wrapEl.insertAdjacentElement('afterEnd', source.wrapEl);
                }
            }
            else {
                if (isFront) {
                    splice({ host: parentObj.children, source, intent: 'start+', cb: (resp) => result = resp[0] });
                    autoFill && parentObj.childrenEl.insertAdjacentElement('afterBegin', source.wrapEl);
                }
                else {
                    splice({ host: parentObj.children, source, intent: 'end+', cb: (resp) => result = resp[0] });
                    autoFill && parentObj.childrenEl.insertAdjacentElement('beforeEnd', source.wrapEl);
                }
            }
        }
        else {
            if (brotherObj && brotherObj.pId === source.pId) {
                let index = treeData.indexOf(brotherObj);
                if (isFront) {
                    splice({ host: treeData, source, index, intent: 'add', cb: (resp) => result = resp[0] });
                    autoFill && brotherObj.wrapEl.insertAdjacentElement('beforeBegin', source.wrapEl);
                }
                else {
                    splice({ host: treeData, source, index: index + 1, intent: 'add', cb: (resp) => result = resp[0] });
                    autoFill && brotherObj.wrapEl.insertAdjacentElement('afterEnd', source.wrapEl);
                }
            }
            else {
                if (!rootEl) {
                    return;
                }
                if (isFront) {
                    splice({ host: treeData, source, intent: 'start+', cb: (resp) => result = resp[0] });
                    autoFill && rootEl.insertAdjacentElement('afterBegin', source.wrapEl);
                }
                else {
                    splice({ host: treeData, source, intent: 'end+', cb: (resp) => result = resp[0] });
                    autoFill && rootEl.insertAdjacentElement('beforeEnd', source.wrapEl);
                }
            }
        }
        cb && cb(result, treeData);
    },
    
    removeBranch: function ({ source, flatData, treeData, cb, }) {
        if (isNull(source) || !flatData || flatData.length === 0) {
            return;
        }
        let obj = findItem(source, flatData), floorStart = treeData[0].floor;
        if (!obj) {
            return;
        }
        if (obj.floor === floorStart) {
            splice({ host: treeData, source: obj, intent: 'remove' });
        }
        else {
            let parent = findItem(obj.pId, flatData);
            if (!parent) {
                return;
            }
            let children = parent.children;
            splice({ host: children, source: obj, intent: 'remove' });
            if (children.length === 0) {
                this.parentToChild(parent);
            }
        }
        obj?.wrapEl && obj.wrapEl.remove();
        cb && cb(obj);
    },
    
    getParentsFromPath({ path, flatData, labelHyphen = config.labelHyphen, field = 'label', pathHyphen = config.pathHyphen, shift = false, pop = false }) {
        let paths = path.split(pathHyphen), parents = paths.map((k) => flatData.find((i) => i.id.toString() == k)).filter(Boolean), text = '';
        shift && parents.shift();
        pop && parents.pop();
        text = parents.map((k) => k[field]).join(labelHyphen);
        return { parents, text };
    },
    
    allToTree: async function ({ content, idStart = config.idStart, rootStart = config.rootStart, floorStart = config.rootStart, pathHyphen = config.pathHyphen, splitHyphen = config.splitHyphen, contType = '', contData = {}, ajax = {}, fill = true, ins, nodeType = 'tree', cb }) {
        if (isEmpty(content)) {
            return [];
        }
        let treeData = [], result = [], getFromNode = (node) => {
            let nodeName = node.nodeName, arr = [];
            if (nodeName === 'SELECT' || nodeName === 'DATALIST') {
                arr = select2Tree(node);
            }
            else if (nodeName === 'UL' || nodeName === 'OL') {
                arr = ul2Tree(node, nodeType);
            }
            else if (nodeName === 'DL') {
                arr = dl2Tree(node);
            }
            else if (nodeName === 'SCRIPT' && node.getAttribute('type') === 'content') {
                arr = this.toTree(parseStr({
                    content: node.textContent,
                    type: 'array',
                    error: (err) => {
                        console.error(config.error.parse, err);
                    }
                }));
            }
            else if (nodeName === 'INPUT') {
                arr = this.fromInput(node, splitHyphen);
            }
            return arr;
        }, getFromArray = (data) => {
            let copyData = deepClone(data);
            return this.toTree(copyData);
        }, strArr2ObjArr = (arr) => {
            let firstType = getDataType(arr[0]), tmp = (firstType === 'String' || firstType === 'Number') ? arr.map((k) => { return { label: k, value: k }; }) : arr;
            return tmp;
        };
        await getContent.call(ins, {
            content,
            contType,
            contData,
            ajax,
            cb: (resp) => {
                let dataType = getDataType(resp);
                if (dataType === 'Object' && resp.hasOwnProperty('data') && Array.isArray(resp.data)) {
                    treeData = getFromArray(resp.data);
                    resp.data = treeData;
                    result = resp;
                }
                else {
                    if (dataType === 'Array') {
                        treeData = getFromArray(strArr2ObjArr(resp));
                    }
                    else if (dataType.includes('HTML')) {
                        if (['UL', 'OL', 'DATALIST', 'DL', 'SELECT', 'SCRIPT'].includes(resp.nodeName)) {
                            treeData = strArr2ObjArr(getFromNode(resp));
                        }
                        else if (resp.nodeName.includes('INPUT')) {
                            treeData = this.fromInput(resp, splitHyphen);
                        }
                    }
                    else if (dataType === 'String') {
                        let tmp = createEl('div', '', resp), node = tmp.querySelector(':scope > ul,:scope > ol,:scope > datalist,:scope > dl,:scope > select,:scope > script,:scope > input');
                        if (node) {
                            treeData = strArr2ObjArr(getFromNode(node));
                            tmp.remove();
                        }
                        else {
                            let str = resp.trim(), type = (str.startsWith('{') && str.endsWith('}')) ? 'object' : (str.startsWith('[') && str.endsWith(']')) ? 'array' : 'other';
                            if (type) {
                                treeData = this.toTree(strArr2ObjArr(parseStr({
                                    content: resp,
                                    type,
                                    error: (err) => {
                                        console.error(config.error.parse, err);
                                    }
                                })));
                            }
                            else {
                                treeData = strArr2ObjArr(resp.split(splitHyphen).filter(Boolean));
                            }
                        }
                    }
                    result = treeData;
                }
            }
        });
        fill && this.addIdPath({ source: treeData, rootStart, idStart, floorStart, pathHyphen });
        cb && cb(result);
        return result;
    },
    getPathHyphen: (treeData) => {
        let item = treeData[0], id = item.id, floor = item.floor, pId = item.pId, path = item.path, hyphen = item.path.replace(pId, '').replace(id, '');
        return { id, floor, pId, path, hyphen };
    },
    graftBranch: function ({ source, target, flatData, treeData, rootEl, isFront = true, isChild = true, cb }) {
        if (isNull(source) || isEmpty(treeData)) {
            return;
        }
        let sourceObj = findItem(source, flatData), targetObj = findItem(target, flatData), firstData = this.getPathHyphen(treeData), floorStart = firstData.floor, rootStart = firstData.pId, pathHyphen = firstData.hyphen;
        if (!sourceObj) {
            return;
        }
        let sourceParent = findItem(sourceObj.pId, flatData);
        if (!sourceParent) {
            splice({ host: treeData, source: sourceObj, intent: 'remove' });
        }
        else {
            splice({ host: sourceParent.children, source: sourceObj, intent: 'remove' });
        }
        if (!targetObj) {
            if (isFront) {
                rootEl?.insertAdjacentElement('afterBegin', sourceObj.wrapEl);
                treeData.unshift(sourceObj);
            }
            else {
                rootEl?.insertAdjacentElement('beforeEnd', sourceObj.wrapEl);
                treeData.push(sourceObj);
            }
            sourceObj.pId = rootStart;
            sourceObj.floor = floorStart;
            sourceObj.path = rootStart + pathHyphen + sourceObj.id;
        }
        else {
            if (isChild) {
                !targetObj.hasOwnProperty('children') && (targetObj.children = []);
                if (isFront) {
                    targetObj.childrenEl.insertAdjacentElement('afterBegin', sourceObj.wrapEl);
                    targetObj.children.unshift(sourceObj);
                }
                else {
                    targetObj.childrenEl.insertAdjacentElement('beforeEnd', sourceObj.wrapEl);
                    targetObj.children.push(sourceObj);
                }
                sourceObj.pId = targetObj.id;
                sourceObj.floor = targetObj.floor + 1;
                sourceObj.path = targetObj.path + pathHyphen + sourceObj.id;
            }
            else {
                if (targetObj.floor === floorStart) {
                    let index = treeData.indexOf(targetObj), id = isFront ? index : index + 1;
                    splice({ host: treeData, source: sourceObj, index: id, intent: 'add' });
                }
                else {
                    let targetParent = findItem(targetObj.pId, flatData), index = targetParent.children.indexOf(targetObj), id = isFront ? index : index + 1;
                    splice({ host: targetParent.children, source: sourceObj, index: id, intent: 'add' });
                }
                sourceObj.pId = targetObj.pId;
                sourceObj.floor = targetObj.floor;
                sourceObj.path = targetObj.path.replace(new RegExp('(.*)' + targetObj.id), '$1' + sourceObj.id);
                let position = isFront ? 'beforeBegin' : 'afterEnd';
                targetObj.wrapEl.insertAdjacentElement(position, sourceObj.wrapEl);
            }
        }
        cb && cb(sourceObj, targetObj);
    }
};

const moveItem = ({ source, target, data, isFront = true }) => {
    let sourceType = getDataType(source), targetType = getDataType(target), from = (sourceType === 'Number') ? source : data.indexOf(source), to = (targetType === 'Number') ? target : data.indexOf(target), sourceObj = sourceType === 'Number' ? data[source] : source, offset = isFront ? 0 : 1;
    if (isNull(from) || isEmpty(data)) {
        throw new Error('Cannot get the index of the moving item!');
    }
    if (from > data.length || from < 0) {
        throw new Error('Exceeded array length!');
    }
    if (to < 0 || to > data.length) {
        to = isFront ? 0 : data.length - 1;
    }
    if (from === to) {
        console.warn('Source and target are the same, not moved!');
        return;
    }
    data.splice(from, 1);
    from < to ? to-- : null;
    data.splice(to + offset, 0, sourceObj);
};

const findItems = (vals, source, key, pio) => {
    let valsType = getDataType(vals), result = [], tmp = (valsType === 'String') ? vals.split(pio?.separator || config.splitHyphen) :
        (valsType === 'Array') ? vals : [vals];
    result = tmp.map((k) => findItem(k, source, key, pio)).filter(Boolean);
    return result;
};

const getAttrBool = (val) => {
    return (val === 'true' || val === '') ? true : false;
};

const getAttrArr = (val, dft = '') => {
    let result = [];
    if (val !== null) {
        let tmp = val.trim();
        try {
            if (tmp === '' || tmp === 'true') {
                result = dft.split(',').filter(Boolean);
            }
            else {
                let val = parseStr({
                    content: tmp,
                    type: 'array',
                    catchable: true,
                });
                result = Array.isArray(val) ? val : [tmp];
            }
        }
        catch (err) {
            result = tmp.split(',');
        }
    }
    return result;
};

let theme = {
    
    set: function (value, name = '') {
        if (isEmpty(value)) {
            return;
        }
        let hexToRgbArr = (val) => {
            val = val.trim();
            let reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/, colorStr = val.toLowerCase(), result = [];
            if (reg.test(colorStr)) {
                if (colorStr.length === 4) {
                    let temp = '#';
                    for (let k = 1; k < 4; k += 1) {
                        temp += colorStr.slice(k, k + 1).concat(colorStr.slice(k, k + 1));
                    }
                    colorStr = temp;
                }
                for (let k = 1; k < 7; k += 2) {
                    result.push(parseInt("0x" + colorStr.slice(k, k + 2)));
                }
            }
            return result;
        }, rgbToHsl = (arr) => {
            let r = arr[0] / 255, g = arr[1] / 255, b = arr[2] / 255, min = Math.min(r, g, b), max = Math.max(r, g, b), h = 0, s, l = (min + max) / 2, diff = max - min;
            if (max === min) {
                h = 0;
                s = 0;
            }
            else {
                s = l > 0.5 ? diff / (2 - max - min) : diff / (max + min);
                switch (max) {
                    case r:
                        h = (g - b) / diff + (g < b ? 6 : 0);
                        break;
                    case g:
                        h = 2 + (b - r) / diff;
                        break;
                    case b:
                        h = 4 + (r - g) / diff;
                        break;
                }
                h = Math.round(h * 60);
            }
            s = Math.round(s * 100);
            l = Math.round(l * 100);
            return arr.length === 4 ? [h, s, l, arr[3]] : [h, s, l, 1];
        }, getHslaArr = (val) => {
            let valType = getDataType(val), result = [];
            if (!isEmpty(val)) {
                if (valType === 'String') {
                    val = val.trim().toLowerCase();
                    if (val.startsWith('hsl')) {
                        result = val.replace(/^(hsl|hsla)\(/, '').replace(/\)$/, '').replace(/\s/g, '').split(',');
                    }
                    else {
                        let temp = [];
                        if (val.startsWith('#')) {
                            temp = hexToRgbArr(val);
                        }
                        else if (val.startsWith('rgb')) {
                            temp = val.replace(/^(rgb|rgba)\(/, '').replace(/\)$/, '').replace(/\s/g, '').split(',').map(Number);
                        }
                        result = rgbToHsl(temp);
                    }
                }
                else if (valType === 'Array') {
                    result = val;
                }
            }
            result.length === 3 ? result.push(1) : null;
            return result;
        }, valueType = getDataType(value);
        let hslaObj = { prim: [], text: [], error: [], succ: [], info: [], warn: [], issue: [] };
        if (valueType === 'Object') {
            for (let k in value) {
                if (value.hasOwnProperty(k) && value[k].length > 2) {
                    hslaObj[k] = getHslaArr(value[k]);
                }
            }
        }
        else {
            hslaObj.prim = getHslaArr(value);
        }
        if (isEmpty(storage.get('AxThemeRaw'))) {
            storage.set('AxThemeRaw', this.get());
        }
        let rootDom = document.querySelector(":root");
        for (let k in hslaObj) {
            if (hslaObj.hasOwnProperty(k) && hslaObj[k].length > 2) {
                rootDom.style.setProperty(`--${prefix}h-${k}`, hslaObj[k][0]);
                rootDom.style.setProperty(`--${prefix}s-${k}`, typeof hslaObj[k][1] === 'string' && hslaObj[k][1].includes('%') ? hslaObj[k][1] : hslaObj[k][1] + '%');
                rootDom.style.setProperty(`--${prefix}l-${k}`, typeof hslaObj[k][2] === 'string' && hslaObj[k][2].includes('%') ? hslaObj[k][2] : hslaObj[k][2] + '%');
                rootDom.style.setProperty(`--${prefix}a-${k}`, hslaObj[k][3]);
            }
        }
        name ? storage.set(name, hslaObj) : null;
    },
    get: function () {
        let getValue = (type) => {
            return [
                getComputedVar(`--${prefix}h-${type}`),
                getComputedVar(`--${prefix}s-${type}`),
                getComputedVar(`--${prefix}l-${type}`),
                getComputedVar(`--${prefix}a-${type}`),
            ].map(k => {
                let val = k.trim();
                return val.includes('%') ? val : Number(val);
            });
        };
        return {
            prim: getValue('prim'),
            text: getValue('text'),
            succ: getValue('succ'),
            error: getValue('error'),
            info: getValue('info'),
            warn: getValue('warn'),
            issue: getValue('issue'),
        };
    },
    reset: function () {
        let themeRaw = storage.get('AxThemeRaw');
        this.set(themeRaw);
    }
};

const attrValBool = (value) => ![null, 'false', '0', false, 0, undefined].includes(value);

const getBetweenEls = ({ selector, start, end, root, exclude }) => {
    let rootEl = getEl(root) || document.body, startEl = getEl(start, rootEl), endEl = getEl(end, rootEl), excEls = getEls(exclude, rootEl), els = getEls(selector, rootEl), result = [];
    if (els.length === 0) {
        return result;
    }
    if (startEl && !endEl) {
        result = els.filter((k) => {
            return startEl.compareDocumentPosition(k) & Node.DOCUMENT_POSITION_FOLLOWING;
        });
    }
    else if (!startEl && endEl) {
        result = els.filter((k) => {
            return endEl.compareDocumentPosition(k) & Node.DOCUMENT_POSITION_PRECEDING;
        });
    }
    else if (startEl && endEl) {
        result = els.filter((k) => {
            return (startEl.compareDocumentPosition(k) & Node.DOCUMENT_POSITION_FOLLOWING) && endEl.compareDocumentPosition(k) & Node.DOCUMENT_POSITION_PRECEDING;
        });
    }
    if (excEls.length) {
        return result.filter((k) => {
            return !excEls.find((i) => i.contains(k));
        });
    }
    else {
        return result;
    }
};

const scrollTo = (elem, options) => {
    let opts = Object.assign({
        curve: 'easeOut',
        offset: 0,
        duration: 0,
    }, options), rootDom = getEl(options?.root), scrollParent = rootDom || window, scrollOffset = toPixel(opts.offset), anchorDom = getEl(elem), bodyScrollTop = document.documentElement.scrollTop || document.body.scrollTop, offsetScrolled = rootDom ? rootDom.scrollTop : bodyScrollTop, initTime = Date.now(), offsetClient = 0;
    if (rootDom && style(rootDom).position === 'static') {
        rootDom.style.position = 'relative';
    }
    if (anchorDom) {
        offsetClient = rootDom ? anchorDom.offsetTop - rootDom.scrollTop : anchorDom.getBoundingClientRect().top;
    }
    else {
        offsetClient = typeof elem === 'number' ? elem - offsetScrolled : -offsetScrolled;
    }
    let duration = !opts.duration || typeof opts.duration !== 'number' ? Math.abs(offsetClient) / 4 + 1000 : opts.duration, repeat = () => {
        let newTime = Date.now() - initTime, timestep = newTime / duration, step = offsetScrolled + (offsetClient - scrollOffset) * curveFns[opts.curve](timestep);
        scrollParent.scrollTo(0, step);
        repeat.timer = requestAnimationFrame(repeat);
        if (newTime >= duration) {
            scrollParent.scrollTo(0, step);
            cancelAnimationFrame(repeat.timer);
        }
    };
    repeat();
    return elem;
};

const setAttr = (el, key, value) => {
    let elem = getEl(el), valType = getDataType(value), result;
    if (!elem || !key)
        return;
    if (isNull(value) || value === false) {
        elem.removeAttribute(key);
        result = { key, value: isNull(value) ? null : false };
    }
    else {
        if (valType === 'Boolean' && value === true) {
            elem.toggleAttribute(key, true);
            result = { key, value: true };
        }
        else if (valType === 'String') {
            elem.setAttribute(key, value);
            result = { key, value };
        }
        else {
            elem.setAttribute(key, JSON.stringify(value));
            result = { key, value: elem.getAttribute(key) };
        }
    }
    return result;
};

const setAttrs = (el, data, cb) => {
    let elem = getEl(el), result = [];
    if (!elem || isEmpty(data))
        return;
    if (!Array.isArray(data) && !data.hasOwnProperty('key') && !data.hasOwnProperty('value')) {
        for (let k in data)
            result.push(setAttr(elem, k, data[k]));
    }
    else {
        let tmp = Array.isArray(data) ? data : [data];
        for (let k of tmp)
            result.push(setAttr(elem, k.key, k.value));
    }
    cb && cb(result.filter(Boolean));
};

const getScrollObj = () => navigator.userAgent.indexOf("Firefox") > 0 ? { event: 'DOMMouseScroll', detail: 'detail' } : { event: 'mousewheel', detail: 'wheelDelta' };

const transformTools = {
    
    get: (elem, props = ['translate', 'scale', 'rotate', 'skew'], instead = false) => {
        let dom = getEl(elem), styles = style(dom), value = dom?.style?.transform, result = {
            rotate: 0,
            scale: { x: 1, y: 1 },
            translate: { x: 0, y: 0 },
            skew: { x: 0, y: 0 }
        };
        if (value) {
            let getReg = (type) => new RegExp(`${type}\\(([^\\)]+)\\)`), getText = (type) => value.match(getReg(type));
            if (props.includes('translate') && value.includes('translate') && getText('translate')) {
                if (getText('translate')[1].includes(',')) {
                    let arr = getText('translate')[1].split(',').map((k) => parseInt(k));
                    result.translate = { x: arr[0], y: arr[1] };
                }
                else {
                    result.translate = { x: parseInt(getText('translate')[1]), y: 0 };
                }
            }
            if (props.includes('scale') && value.includes('scale') && getText('scale')) {
                if (getText('scale')[1].includes(',')) {
                    let arr = getText('scale')[1].split(',').map((k) => parseFloat(k));
                    result.scale = { x: arr[0], y: arr[1] };
                }
                else {
                    result.scale = { x: parseFloat(getText('scale')[1]), y: parseFloat(getText('scale')[1]) };
                }
            }
            if (props.includes('rotate') && value.includes('rotate') && getText('rotate')) {
                result.rotate = parseFloat(getText('rotate')[1]);
            }
            if (props.includes('skew') && value.includes('skew') && getText('skew')) {
                if (getText('skew')[1].includes(',')) {
                    let arr = getText('skew')[1].split(',').map((k) => parseFloat(k));
                    result.skew = { x: arr[0], y: arr[1] };
                }
                else {
                    result.skew = { x: parseFloat(getText('skew')[1]), y: 0 };
                }
            }
        }
        if (instead) {
            result.translate = { x: parseInt(styles.left), y: parseInt(styles.top) };
        }
        return result;
    },
    
    set: function ({ el, data, instead = false, cb }) {
        let dom = getEl(el), now = this.get(dom), transformTxt = dom?.style.transform || '', getReg = (type) => { return new RegExp(`${type}\\(([^\\)]+)\\)`, 'gmi'); }, re = (val, label, prop) => isNull(val[prop]) ? now[label][prop] : val[prop];
        if (!dom && isEmpty(data)) {
            return;
        }
        for (let k in data) {
            if (data.hasOwnProperty(k)) {
                let value = '';
                if (k === 'translate') {
                    if (instead) {
                        !isNaN(data[k]) ?
                            (dom.style.left = `${data[k]}px`) :
                            (dom.style.left = `${re(data[k], 'translate', 'x')}px`, dom.style.top = `${re(data[k], 'translate', 'y')}px`);
                    }
                    else {
                        value = !isNaN(data[k]) ? `${k}(${data[k]}px)` : `${k}(${re(data[k], 'translate', 'x')}px,${re(data[k], 'translate', 'y')}px)`;
                        if (transformTxt.includes(k)) {
                            if (transformTxt.startsWith('translate')) {
                                transformTxt = transformTxt.replace(getReg(k), value);
                            }
                            else {
                                transformTxt = transformTxt.replace(getReg(k), '');
                                transformTxt = `${value} ${transformTxt}`;
                            }
                        }
                        else {
                            transformTxt = `${value} ${transformTxt}`;
                        }
                    }
                }
                else {
                    if (k === 'scale') {
                        value = !isNaN(data[k]) ? `${k}(${data[k]})` : `${k}(${re(data[k], 'scale', 'x')},${re(data[k], 'scale', 'y')})`;
                    }
                    else if (k === 'rotate') {
                        value = `${k}(${data[k]}deg)`;
                    }
                    else if (k === 'skew') {
                        value = !isNaN(data[k]) ? `${k}(${data[k]}deg)` : `${k}(${re(data[k], 'skew', 'x')}deg,${re(data[k], 'skew', 'y')}deg)`;
                    }
                    if (transformTxt.includes(k)) {
                        transformTxt = transformTxt.replace(getReg(k), value);
                    }
                    else {
                        transformTxt += ` ${value}`;
                    }
                }
            }
        }
        dom.style.transform = transformTxt;
        cb && cb(transformTxt);
    },
    remove: ({ el, prop }) => {
        let dom = getEl(el), tmp = dom?.style?.transform;
        if (!dom || !tmp)
            return;
        dom.style.transform = prop ? tmp.replace(new RegExp(`${prop}\\([^)]+\\)\\s*`, 'g'), '') : 'none';
    },
    disable: (el) => {
        let dom = getEl(el);
        if (!dom)
            return;
        dom.style.transitionDuration = `0ms`;
    },
    enable: (el, duration = 200) => {
        let dom = getEl(el);
        if (!dom)
            return;
        dom.style.transitionDuration = `${duration}ms`;
    },
    clear: (el) => {
        let dom = getEl(el);
        if (!dom)
            return;
        dom.style.transition = `none`;
    },
    
    matrix: (elem) => {
        let el = getEl(elem);
        if (!el)
            return [];
        let value = style(el).transform;
        return (value === 'none') ? [1, 0, 0, 1, 0, 0] : value.replace(/[^0-9.\-,]/g, '').split(',').map(k => Number(k));
    },
};

const isScrollUp = (event, detailName = getScrollObj().detail) => {
    let mouseDetail = event[detailName], isUp;
    if (navigator.userAgent.indexOf("Firefox") > 0) {
        isUp = mouseDetail > 0 ? true : false;
    }
    else {
        isUp = mouseDetail < 0 ? true : false;
    }
    return isUp;
};

const eventMap = isMobi ? ['touchstart', 'touchmove', 'touchend', 'touchcancel'] : ['mousedown', 'mousemove', 'mouseup', 'mouseleave'];

const getClientObj = (event) => {
    let tmp = isMobi ? event.targetTouches[0] : event;
    return tmp ? { x: tmp.clientX, y: tmp.clientY } : null;
};

const getNestProp = (data, path) => {
    if (!path)
        return data;
    return path.replace(/['"\s]/g, '').split('.').reduce((prev, cur) => {
        if (prev && prev[cur] !== undefined)
            return prev[cur];
        if (cur.endsWith(']')) {
            let tmp = [];
            if (cur.startsWith('[')) {
                tmp = cur.split(']').map((k) => k.replace('[', '')).filter(Boolean);
            }
            else {
                tmp = cur.split('[').map((k) => k.replace(']', ''));
            }
            return tmp.reduce((a, b) => a && a[b], prev);
        }
        return undefined;
    }, data);
};

const arrSort = (source, basis) => {
    if (isEmpty(source))
        return;
    let basisType = getDataType(basis), _basis = [], basisFn = (keys) => {
        if (keys.length === 0)
            return (a, b) => (a + '').localeCompare(b + '', config.lang.name);
        return (a, b) => {
            for (let k of keys) {
                let key = '', dir = 0, result = 0;
                if (k.hasOwnProperty('key')) {
                    key = k.key;
                }
                else {
                    key = 'label';
                }
                if (k.hasOwnProperty('dir')) {
                    dir = k.dir === 'asc' ? 1 : k.dir === 'desc' ? -1 : 0;
                }
                else {
                    dir = 1;
                }
                result = (getNestProp(a, key) + '').localeCompare(getNestProp(b, key) + '', k.locale || config.lang.name, k.opt || {});
                if (result !== 0)
                    return result * dir;
            }
            return 0;
        };
    };
    if (basisType === 'Object') {
        _basis = [basis];
    }
    else if (basisType === 'Array') {
        _basis = basis;
    }
    source.sort(basisFn(_basis));
};

const clearRegx = (data) => (data + '').replace(/[.*+?^${}()|[\]\\]/g, '');

const arrSearch = ({ keys, props = 'label', source = [], fuzzy = true, limit = 0, order = '', ignore = true, hyphen = config.splitHyphen }) => {
    if (isEmpty(keys) || isEmpty(props) || isEmpty(source)) {
        return [];
    }
    let keysArr = valToArr(keys, hyphen).map((k) => clearRegx(k)).filter(Boolean), propsArr = valToArr(props, hyphen), crudeArr = [], regStr = '(' + keysArr.join('|') + ')', regExp = new RegExp(regStr, `${ignore ? 'i' : ''}g`), lowerSet = (arr) => [...new Set(arr.map((k) => k.toLowerCase()))];
    for (let v of source) {
        let valueStr = propsArr.map(k => v[k]).join('■'), valueMatch = valueStr.match(regExp), valueObj = {}, keysMatch = [];
        if (valueMatch) {
            keysMatch = [...new Set(valueMatch)];
            valueObj = { source: v, weight: valueMatch.length, keys: keysMatch };
            if (!fuzzy) {
                if (ignore) {
                    (lowerSet(keysArr).length === lowerSet(keysMatch).length) && crudeArr.push(valueObj);
                }
                else {
                    (keysArr.length === keysMatch.length) && crudeArr.push(valueObj);
                }
            }
            else {
                crudeArr.push(valueObj);
            }
        }
    }
    if (order) {
        let orderType = getDataType(order), basis;
        if (orderType === 'String') {
            basis = order === 'desc' ? { key: 'weight', dir: 'desc' } : order === 'asc' ? { key: 'weight', dir: 'asc' } : '';
        }
        else if (orderType === 'Boolean' && order === true) {
            basis = { key: 'weight', dir: 'desc' };
        }
        else {
            basis = order;
        }
        arrSort(crudeArr, basis);
    }
    (limit && crudeArr.length > limit) && crudeArr.splice(limit);
    return crudeArr;
};

const getIntArr = (data, hyphen = config.splitHyphen) => {
    let arrs = data.map((k) => {
        return typeof k === 'string' ? valToArr(k, hyphen) : k;
    });
    return arrs.reduce((prev, cur) => prev.filter((k) => cur.includes(k)));
};

const isOutside = (ev, elem) => {
    let el = getEl(elem);
    if (!el || !ev)
        return true;
    let point = isMobi ? { x: ev.targetTouches[0].clientX, y: ev.targetTouches[0].clientY } : { x: ev.clientX, y: ev.clientY }, lt = { x: el.getBoundingClientRect().left, y: el.getBoundingClientRect().top }, rb = { x: lt.x + el.offsetWidth, y: lt.y + el.offsetHeight };
    return (point.x < lt.x || point.x > rb.x || point.y < lt.y || point.y > rb.y) ? true : false;
};

const getRectPoints = (ev, elem) => {
    let el = getEl(elem), result = [];
    if (!el || !ev)
        return result;
    let getter = (data, axis) => {
        let prop = `client${axis}`;
        return data.targetTouches && data.targetTouches[0] ? data.targetTouches[0][prop] : data.changedTouches ? data.changedTouches[0][prop] : data[prop];
    }, point = { x: getter(ev, 'X'), y: getter(ev, 'Y') }, x = point.x, y = point.y, size = { w: el.offsetWidth, h: el.offsetHeight }, lt = { x: el.getBoundingClientRect().left, y: el.getBoundingClientRect().top }, rb = { x: lt.x + size.w, y: lt.y + size.h }, range = { 'h1/3': lt.y + size.h / 3, 'h1/2': lt.y + size.h / 2, 'h2/3': lt.y + size.h * 2 / 3, 'w1/3': lt.x + size.w / 3, 'w1/2': lt.x + size.w / 2, 'w2/3': lt.x + size.w * 2 / 3 };
    if (x >= lt.x && x <= rb.x && y >= lt.y && y <= rb.y) {
        result.push('inside');
        if (y < range['h1/3']) {
            result.push('t/3');
        }
        else if (y >= range['h1/3'] && y < range['h1/2']) {
            result.push('t/2');
        }
        else if (y >= range['h2/3']) {
            result.push('b/3');
        }
        else if (y >= range['h1/2']) {
            result.push('b/2');
        }
        if (x < range['w1/3']) {
            result.push('l/3');
        }
        else if (x >= range['w1/3'] && x < range['w1/2']) {
            result.push('l/2');
        }
        else if (x >= range['w2/3']) {
            result.push('r/3');
        }
        else if (x >= range['w1/2']) {
            result.push('r/2');
        }
    }
    else {
        result = ['outside'];
    }
    return result;
};

const elsSort = (data, reverse = false) => {
    let tmp = data.map((k) => getEl(k)).filter(Boolean), els = unique(tmp), result = [];
    let topo = new Map();
    for (let k of els) {
        topo.set(k, []);
    }
    for (let k of els) {
        for (let i of els) {
            if (k !== i && k.contains(i)) {
                topo.get(k).push(i);
            }
        }
    }
    for (let k of topo) {
        k[1].length > 0 && result.push(...k[1]);
        result.push(k[0]);
    }
    return reverse ? result.reverse() : result;
};

const getStrFromTpl = function (data, tplStr, tplEng) {
    let str = tplStr || this.tplStr, eng = tplEng || this.tplEng;
    return eng.name === 'template' ? eng(str)(data) : eng(str, data);
};

const setSingleSel = (target, sel) => {
    let arr = Array.isArray(target) ? target : [target], els = arr.map((k) => getEls(k)).flat().filter(Boolean), setFn = (el) => {
        if (sel.trim().startsWith('#')) {
            el.id = sel.slice(1);
        }
        else if (sel.trim().startsWith('.')) {
            el.classList.add(sel.slice(1));
        }
        else if (sel.trim().startsWith('[') && sel.trim().endsWith(']')) {
            let tmp = sel.slice(1, -1).replace('"', '').replace("'", '').split('=');
            el.setAttribute(tmp[0], tmp[1] || '');
        }
    };
    if (els.length === 0 || !sel || typeof sel !== 'string')
        return;
    for (let k of els) {
        setFn(k);
    }
};

const splitNum = (value) => {
    let num = value.toString().split('.'), str = `<s>${num[0]}</s>`;
    !isNull(num[1]) && (str += `<u>.${num[1]}</u>`);
    return `<span class="${prefix}splitnum">${str}</span>`;
};

const parseUrlArr = (url, opts = {}) => {
    if (!url)
        return [];
    let options = Object.assign({ start: '{', end: '}', max: 20 }, opts), result = [], reg = new RegExp(`\\${options.start}(\\S*)\\${options.end}`), match = url.match(reg);
    if (!match)
        return [];
    let tmp = match[1], pageRange = tmp.split('-').map((k) => ~~k), pageCount = tmp.split(',').map((k) => ~~k), pageStart = pageRange[0], pageEnd = pageRange[1], pageMax = pageEnd || pageStart + options.max, holder = `${options.start}${tmp}${options.end}`;
    if (url.includes(',')) {
        result = pageCount.map((k) => url.replace(holder, k));
    }
    else {
        for (let i = pageStart; i <= pageMax; i++) {
            result.push(url.replace(holder, i));
        }
    }
    return result;
};

const dateTools = {
    toSlash: (data) => {
        return data.replaceAll('-', '/');
    },
    getTimeObj: function (str) {
        let tmp = str.replaceAll(' ', '').split(':'), result = { hh: 0, mm: 0, ss: 0 };
        if (str) {
            if (tmp.length < 2) {
                result.hh = ~~str || 0;
            }
            else {
                result.hh = ~~tmp[0] || 0;
                result.mm = ~~tmp[1] || 0;
                result.ss = ~~tmp[2] || 0;
            }
        }
        return result;
    },
    int2Date: (value, type = 'datetime') => {
        value = ~~value;
        let prop = (type === 'daytime') ? 'hh' : (type === 'month') ? 'MM' : 'YYYY';
        return { value, prop };
    },
    getDate: function (data, type = 'datetime', lang = config.lang.datetime) {
        let dateType = getDataType(data), result;
        if (isEmpty(data)) {
            result = new Date();
        }
        else {
            if (dateType === 'Date') {
                result = new Date(data.valueOf());
            }
            else if (dateType === 'String') {
                let isBc = data.includes(lang.bc);
                data = this.toSlash(trim(data).replace(lang.bc, ''));
                if (data.includes(' ')) {
                    result = new Date(data);
                    if (!data.includes('/')) {
                        console.warn('Invalid date format, current date used instead!');
                        result = new Date();
                    }
                    else {
                        let tmp = data.split(' '), tmpStart = tmp[0].split('/'), tmpEnd = tmp[1].split(':'), tmpFlat, tmpDate;
                        tmpStart[1] = tmpStart[1] ? tmpStart[1] - 1 : 0;
                        tmpStart[2] = tmpStart[2] || 0;
                        tmpEnd[1] = tmpEnd[1] || 0;
                        tmpEnd[2] = tmpEnd[2] || 0;
                        tmpFlat = [...tmpStart, ...tmpEnd].map((k) => ~~k);
                        isBc && (tmpFlat[0] = tmpFlat[0] * -1);
                        tmpDate = new Date(...tmpFlat);
                        tmpDate.setFullYear(tmpFlat[0]);
                        result = tmpDate;
                    }
                }
                else {
                    if (data.includes(':')) {
                        result = new Date('1970/1/1 ' + data);
                    }
                    else if (data.includes('/')) {
                        let tmp = data.split('/').map((k) => ~~k);
                        tmp[1] && tmp[1]--;
                        isBc && (tmp[0] = tmp[0] * -1);
                        result = new Date(...tmp);
                    }
                    else {
                        result = new Date('1970/1/1 00:00:00');
                        let tmp = this.int2Date(data, type);
                        tmp.prop === 'hh' ? result.setHours(tmp.value) : tmp.prop === 'MM' ? result.setMonth(tmp.value - 1) : result.setFullYear(isBc ? tmp.value * -1 : tmp.value);
                    }
                }
            }
            else if (dateType === 'Object') {
                result = new Date('1970/1/1 00:00:00');
                data.hasOwnProperty('YYYY') && result.setFullYear(data.YYYY);
                data.hasOwnProperty('MM') && result.setMonth(data.MM);
                data.hasOwnProperty('DD') && result.setDate(data.DD);
                data.hasOwnProperty('hh') && result.setHours(data.hh);
                data.hasOwnProperty('mm') && result.setMinutes(data.mm);
                data.hasOwnProperty('ss') && result.setSeconds(data.ss);
            }
            else {
                result = new Date();
            }
        }
        if (['date', 'year', 'month'].includes(type)) {
            result.setHours(0);
            result.setMinutes(0);
            result.setSeconds(0);
            ['year', 'month'].includes(type) && result.setDate(1);
            type === 'year' && result.setMonth(0);
        }
        else if (type === 'daytime') {
            result.setFullYear(1970);
            result.setMonth(0);
            result.setDate(1);
        }
        return result;
    },
    getDateObj: function (data, type = 'datetime') {
        let dataType = getDataType(data), date, result;
        if (dataType === 'Object') {
            result = { ...data };
        }
        else {
            date = this.getDate(data, type);
            result = { YYYY: date.getFullYear(), MM: date.getMonth(), DD: date.getDate(), hh: date.getHours(), mm: date.getMinutes(), ss: date.getSeconds() };
        }
        if (['date', 'year', 'month'].includes(type)) {
            Reflect.deleteProperty(result, 'hh');
            Reflect.deleteProperty(result, 'mm');
            Reflect.deleteProperty(result, 'ss');
            ['year', 'month'].includes(type) && Reflect.deleteProperty(result, 'DD');
            type === 'year' && Reflect.deleteProperty(result, 'MM');
        }
        else if (type === 'daytime') {
            Reflect.deleteProperty(result, 'YYYY');
            Reflect.deleteProperty(result, 'MM');
            Reflect.deleteProperty(result, 'DD');
        }
        return result;
    },
    getCurDays: function (data) {
        let curDate = this.getDate(data), curYear = curDate.getFullYear(), curMonth = curDate.getMonth();
        return new Date(curYear, curMonth + 1, 0).getDate();
    },
    getOffsetDate: function (data, options = { YYYY: 0, MM: 0, DD: 0, hh: 0, mm: 0, ss: 0 }) {
        let curDate = this.getDate(data);
        options.hasOwnProperty('ss') && curDate.setDate(curDate.getSeconds() + options.ss);
        options.hasOwnProperty('mm') && curDate.setDate(curDate.getMinutes() + options.mm);
        options.hasOwnProperty('hh') && curDate.setDate(curDate.getHours() + options.hh);
        options.hasOwnProperty('DD') && curDate.setDate(curDate.getDate() + options.DD);
        options.hasOwnProperty('MM') && curDate.setMonth(curDate.getMonth() + options.MM);
        options.hasOwnProperty('YYYY') && curDate.setFullYear(curDate.getFullYear() + options.YYYY);
        return curDate;
    },
    fillZero: (data, places = 2) => data.toString().padStart(places, '0'),
    fillFormat: function (date, format, lang = config.lang.datetime) {
        let YYYY, MM, DD, hh, mm, ss, WW, weeks = lang.weeks, tmp = this.getDate(date);
        YYYY = tmp.getFullYear();
        MM = tmp.getMonth() + 1;
        DD = tmp.getDate();
        WW = tmp.getDay();
        hh = tmp.getHours();
        mm = tmp.getMinutes();
        ss = tmp.getSeconds();
        if (format.includes('YYYY')) {
            let tmp = Math.abs(YYYY) + '';
            if (YYYY < 0) {
                if (format.includes('BC')) {
                    format = format.replace('BC', lang.bc);
                }
                else {
                    tmp = lang.bc + tmp;
                }
            }
            else {
                format.includes('BC') && (format = format.replace('BC', ''));
            }
            format = format.replace('YYYY', tmp);
        }
        format.includes('MM') && (format = format.replace('MM', this.fillZero(MM)));
        format.includes('DD') && (format = format.replace('DD', this.fillZero(DD)));
        format.includes('hh') && (format = format.replace('hh', this.fillZero(hh)));
        format.includes('mm') && (format = format.replace('mm', this.fillZero(mm)));
        format.includes('ss') && (format = format.replace('ss', this.fillZero(ss)));
        format.includes('WW') && (format = format.replace('WW', weeks[WW]));
        return format;
    },
    sort: function (data, order = 'asc') {
        return data.sort((a, b) => order === 'asc' ? this.getDate(a) - this.getDate(b) : this.getDate(b) - this.getDate(a));
    },
    getDateType: (format) => {
        return RegExp(/^(?=.*YYYY)(?=.*MM)(?=.*DD)(?=.*hh).*$/).test(format) ? 'datetime' :
            RegExp(/^(?=.*YYYY)(?=.*MM)(?=.*DD).*$/).test(format) ? 'date' :
                RegExp(/^(?=.*YYYY)(?=.*MM).*$/).test(format) ? 'month' :
                    RegExp(/^(?=.*YYYY).*$/).test(format) ? 'year' :
                        RegExp(/^(?=.*hh).*$/).test(format) ? 'daytime' : '';
    },
    isSameDay: (a, b) => {
        
        return a.YYYY == b.YYYY && a.MM == b.MM && a.DD == b.DD;
        
    }
};

const repeatStr = (tpl, num = 1, data) => {
    let result = '';
    for (let i = 1; i <= num; i++) {
        result += renderTpl(tpl, { index: i, data });
    }
    return result;
};

const createModule = (options) => {
    let opts = Object.assign({ type: 'none', optMaps: [], component: false, spread: [], methods: {}, ready: null, init: null, augment: false, initial: true }, options), modName = opts.name || 'Module' + Date.now(), hasHost = ['node', 'data'].includes(opts.type), hostType = hasHost ? opts.type : 'none', str = `"use strict";
            return class ${modName} extends ModBaseListenCache {
                static hostType = "${hostType}";
                static optMaps =  optMaps;
                constructor(${hasHost ? 'host,' : ''}options, eager = initial) {
                    super();
                    super.ready({ type:"${hostType}",${hasHost ? 'host,' : ''} options, optMaps:${modName}.optMaps,  component, spread });
                    constructed && constructed.call(this);
                    super.listen({ name: 'constructed' });
                    eager && this.init();
                }
                async init(cb) {
                    super.listen({ name: 'initiate' });
                    try{
                        this.options.b4Init && await this.options.b4Init.call(this);
                    }catch{
                        console.warn(config.warn.init);
                        return this;
                    }
                    initiated && await initiated.call(this);
                    super.listen({ name: 'initiated', cb });
                    return this;
                }
            }
        `, module = new Function('optMaps', 'component', 'spread', 'constructed', 'initiated', 'initial', 'ModBaseListenCache', 'config', str)(opts.optMaps, opts.component, opts.spread, opts.constructed, opts.initiated, opts.initial, ModBaseListenCache, config);
    for (let k in opts.methods) {
        module.prototype[k] = opts.methods[k];
    }
    opts.augment && (ax[modName] = module);
    return module;
};

class CompBase extends HTMLElement {
    shadowEl;
    slotEl;
    propsRaw;
    properties;
    propsProxy;
    propsObs;
    custAttrs;
    boolAttrs;
    reset;
    clear;
    wrapEl;
    plans;
    connected;
    set;
    timestamp;
    connCount;
    rawHtml;
    canListen;
    sourceEl;
    ins;
    constructor() {
        super();
        this.properties = {};
        this.propsRaw = {};
        this[ax.compSign] = true;
        this.plans = {};
        this.timestamp = Date.now();
        this.connCount = 0;
        this.createPropsObs();
        this.canListen = true;
        this.getRawCont();
    }
    connectedCallback() {
        this.connectedRender();
    }
    disconnectedCallback() {
        this.listen({ name: 'disconnected' });
    }
    adoptedCallback() {
        this.listen({ name: 'adopted' });
    }
    static lazyAttrs = ['stor-name', 'class'];
    static baseAttrs = ['on-connected', 'on-disconnected', 'on-adopted', 'on-reset', 'on-set'];
    static jsonAttrs = ['lang'];
    static boolAttrs = [];
    createShadow() {
        this.shadowEl = this.attachShadow({ mode: "open" });
        this.slotEl = createEl('slot');
        this.shadowEl.appendChild(this.slotEl);
    }
    saveProps(name, newVal, comp) {
        this.propsProxy[name] = comp.boolAttrs.includes(name) ? getAttrBool(newVal) : comp.jsonAttrs.includes(name) ? strToJson(newVal) : newVal;
    }
    savePropsToListen(name, oldVal, newVal, comp, other) {
        if (other && Object.keys(other).includes(name)) {
            this.propsProxy[name] = other[name];
        }
        else {
            this.saveProps(name, newVal, comp);
        }
        this.changedMaps[name] && this.changedMaps[name].call(this, { name, newVal, oldVal, proxy: this.propsProxy });
    }
    getRawProps(comp) {
        for (let k of [...comp.custAttrs, ...comp.lazyAttrs])
            this.propsRaw[k] = this.getAttribute(k);
        for (let k of comp.boolAttrs)
            this.propsRaw[k] = getAttrBool(this.getAttribute(k));
        for (let k of comp.jsonAttrs)
            this.propsRaw[k] = strToJson(this.getAttribute(k));
    }
    getProxyProps() {
        for (let k in this.propsRaw)
            this.propsProxy[k] = this.propsRaw[k];
    }
    restoreAttrs() {
        for (let k in this.propsProxy) {
            let valProxy = this.propsProxy[k], valRaw = this.propsRaw[k];
            if (valProxy !== valRaw) {
                if (isNull(valRaw)) {
                    this.removeAttribute(k);
                }
                else if (valRaw === true) {
                    this.setAttribute(k, '');
                }
                else if (valRaw === false) {
                    this.removeAttribute(k);
                }
                else {
                    this.setAttribute(k, valRaw);
                }
            }
        }
    }
    getHtmlVal(name = 'label', unformatted = true) {
        let val = unformatted ? this.textContent?.trim() : this.innerHTML;
        return val || this.getAttribute(name);
    }
    connectedRender(data) {
        this.render(data);
        this.connected = true;
        this.listen({ name: 'connected', params: [this.ins] });
        this.connCount++;
    }
    getConnCont(format = 'html') {
        let tmp = createEl('div', null, this.rawHtml);
        return format === 'text' ? tmp.textContent : this.rawHtml;
    }
    getRawCont() {
        if (!this.connCount) {
            let tmp = this.querySelector(`:scope>[${alias}="source"]`);
            if (tmp) {
                this.rawHtml = tmp.innerHTML;
            }
            else {
                this.sourceEl = createEl('template', { [alias]: 'source' });
                this.sourceEl.innerHTML = this.rawHtml = this.innerHTML;
            }
        }
    }
    insertSource() {
        let tmpSourceEl = this.sourceEl || this.querySelector(`:scope>[${alias}="source"]`);
        this.innerHTML = tmpSourceEl.outerHTML || '';
    }
    createPropsObs() {
        this.propsObs = new Observe(this.properties, { deep: true });
        this.propsProxy = this.propsObs.proxy;
    }
    updateProxy(name, newVal, map) {
        let value;
        if (newVal === null) {
            this.propsProxy[name] = this.ins.rawOpts[name];
        }
        else {
            if (name === 'options') {
                value = strToJson(newVal);
            }
            else {
                if (!map)
                    throw new Error(`Setting ${name} is invalid , Please use a module and set the map for options!`);
                value = attrJoinVal(name, newVal, map);
            }
            deepMerge(this.propsProxy, value, {});
        }
    }
    changedMaps = {};
}

class CompBaseComm extends CompBase {
    constructor() {
        super();
        this.reset = function () {
            this.innerHTML = '';
            this.fillWrap(this.propsRaw);
            this.restoreAttrs();
            this.render(this.propsRaw);
            this.listen({ name: 'reset' });
        };
        this.clear = (attr = 'value') => {
            this.removeAttribute(attr);
            this.listen({ name: 'cleared', params: [attr] });
        };
        this.set = (data) => {
            if (isEmpty(data))
                return;
            let tmp = (typeof data === 'string') ? strToJson(data) : data;
            setAttrs(this, tmp, (resp) => {
                this.listen({ name: 'set', params: [resp] });
            });
        };
        this.on('disconnected', () => {
            this.connCount = 0;
            this.innerHTML = this.rawHtml;
        });
        this.on('adopted', () => {
            this.connCount = 0;
            this.innerHTML = this.rawHtml;
        });
        this.on('connected', () => {
            this.setAttribute(ax.compSign, '');
            this.setCache();
        });
        this.on('reset', () => {
            this.clearCache();
        });
        this.propsObs.on('set', (data) => {
            this.connected && this.updateCache({ [data.key]: data.value });
        });
    }
    listen({ name, params = [], cb } = {}) {
        name && this.plans.hasOwnProperty(name) ? this.emit(name, ...params) : null;
        if (name && this.propsProxy[`on-${name.toLowerCase()}`]) {
            let value = this.propsProxy[`on-${name.toLowerCase()}`], addFnShell = (str) => {
                let tmp = str.trim(), result = (!(tmp.startsWith('function') || tmp.startsWith('(')) && !tmp.endsWith('}')) ? `function(){${str}}` : str;
                return result;
            }, fn = parseStr({
                content: addFnShell(value),
                type: 'other',
                method: 'new Function'
            });
            fn && fn.call(this, ...params);
        }
        cb && cb.call(this, ...params);
    }
    on(type, handler) {
        plan.add(type, this, handler);
        return this;
    }
    emit(type, ...params) {
        plan.do(type, this, ...params);
        return this;
    }
    off(type, handler) {
        plan.remove(type, this, handler);
        return this;
    }
    setCache() {
        let storName = this.propsProxy['stor-name'], cache = storage.get(storName);
        if (!storName || !cache)
            return;
        let copyCache = deepClone(cache), dataCache = copyCache['data'], hasData = copyCache.hasOwnProperty('data');
        if (hasData) {
            dataCache ? setAttr(this, 'data', dataCache) : this.removeAttribute('data');
            Reflect.deleteProperty(copyCache, 'data');
        }
        for (let k in copyCache) {
            setAttr(this, k, copyCache[k]);
        }
    }
    updateCache(data) {
        let storName = this.propsProxy['stor-name'];
        if (!storName)
            return;
        let cache = storage.get(storName) || {};
        Object.assign(cache, data);
        storage.set(storName, cache);
    }
    clearCache() {
        let storName = this.propsProxy['stor-name'];
        if (!storName)
            return;
        storage.clear(storName);
    }
}

const createComp = (options) => {
    let opts = Object.assign({ attrs: [], methods: {}, excludeAttrs: [], modOpts: {}, augment: false, autoUpdate: true, autoIns: true, register: true }, options), elem = window.customElements.get(opts.tagName);
    if (elem) {
        console.warn(`${opts.tagName} is already defined!`);
        return elem;
    }
    let compName = opts.compName || 'Comp' + Date.now(), hostName = opts.hostName || 'div', optMaps = opts.module ? opts.module.optMaps : [], attrs = opts.module ? [...optMaps.map((k) => k.attr), ...opts.attrs] : opts.attrs, str = `"use strict";
            return class ${compName} extends CompBaseComm {
                constructor() {
                    super();
                    this.createShadow();
                    this.createPropsObs();
                    this.optMaps = optMaps;
                    this.canUpdate = true;
                    for (let k of [...attrs, 'options']) this.propsRaw[k] = this.getAttribute(k);
                    this.on('connected',()=>{
                        connected && connected.call(this);
                    });
                    this.reset = () => {
                        this.restoreAttrs();
                        this.listen({ name: 'reset' });
                    }
                    for (let k in methods) this[k] = methods[k].bind(this);
                    constructed && constructed.call(this);
                    this.listen({ name: 'constructed'});
                }
                static get observedAttributes() {
                    return [...attrs, 'options'];
                }
                attributeChangedCallback(name, oldVal, newVal) {
                    if (!this.canListen) return;
                    !excludeAttrs.includes(name) && this.updateProxy(name, newVal,this.optMaps);
                    changed && changed.call(this,{name, oldVal, newVal});
                    this.listen({ name: 'changed', params: [{name, oldVal, newVal}] });
                }
                render() {
                    this.insertSource();
                    this.wrapEl = createEl('${hostName}',{${alias}:'slot-host'}, this.rawHtml);
                    this.appendChild(this.wrapEl);
                    autoIns && module && (this.ins = new module(this.wrapEl,modOpts));
                    this.propsObs.on('completed', (data) => {
                        if(this.connCount > 1){
                            autoUpdate && this?.ins?.update && this.ins.update(this.properties);
                            updated && updated.call(this,data);
                            this.listen({ name: 'updated',params:[data]});
                        }
                        this.listen({ name: 'completed',params:[data]});
                    });
                }
            }
        `, component = new Function('constructed', 'connected', 'changed', 'updated', 'autoUpdate', 'autoIns', 'excludeAttrs', 'module', 'modOpts', 'CompBaseComm', 'createEl', 'attrs', 'optMaps', 'methods', str)(opts.constructed, opts.connected, opts.changed, opts.updated, opts.autoUpdate, opts.autoIns, opts.excludeAttrs, opts.module, opts.modOpts, CompBaseComm, createEl, attrs, optMaps, opts.methods);
    opts.register && window.customElements.define(opts.tagName, component);
    opts.augment && (ax[compName] = component);
    return component;
};

const getArrMap = (data, key) => {
    if (!Array.isArray(data) || !data.length)
        return null;
    let map = new Map();
    for (let k of data) {
        let prop = key ? k[key] : k;
        !map.has(k[prop]) && map.set(prop, k);
    }
    return map;
};

const includes = (data, value) => {
    if (!Array.isArray(data) || !data.length)
        return false;
    let set = new Set(data), result = set.has(value);
    set.clear();
    return result;
};

const capStart = (str) => {
    let idx = str.search(/[a-zA-Z]/);
    if (idx > -1) {
        let char = str.charAt(idx);
        return str.replace(char, char.toUpperCase());
    }
    else {
        return str;
    }
};

const removeItem = (data, item) => {
    if (!Array.isArray(data) || !data.length || !item)
        return;
    for (let i = 0; i < data.length; i++) {
        if (data[i] === item) {
            data.splice(i, 1);
            break;
        }
    }
};

const appendEls = ({ parent, nodes, reverse = false, target, prepend = false }) => {
    let fragment = document.createDocumentFragment(), tmp = Array.isArray(nodes) ? nodes : [nodes], data = tmp.filter((k) => k && k.nodeType && (k.nodeType === k.ELEMENT_NODE)), host = getEl(parent), targetEl = getEl(target, host), refer;
    if (!host || !data.length)
        return;
    for (let k of data)
        reverse ? fragment.prepend(k) : fragment.appendChild(k);
    if (targetEl) {
        refer = prepend ? targetEl : targetEl.nextElementSibling;
    }
    else {
        refer = prepend ? host.firstElementChild : null;
    }
    host.insertBefore(fragment, refer);
};

const decompTask = ({ tasks, run, count = 20, cb }) => {
    if (!Array.isArray(tasks) || !tasks.length)
        return;
    let idx = 0, handler = () => {
        let end = Math.min(idx + count, tasks.length);
        for (let i = idx; i < end; i++) {
            run && run(tasks[i]);
            i === tasks.length - 1 && cb && cb();
        }
        idx = end;
        if (idx < tasks.length)
            requestAnimationFrame(handler);
    };
    requestAnimationFrame(handler);
};

const filterPrims = (data) => {
    let filter = (obj) => {
        return Object.entries(obj).reduce((start, [key, value]) => {
            if (['number', 'string', 'boolean', 'undefined', 'bigint', 'symbol'].includes(typeof value)) {
                start[key] = value;
            }
            return start;
        }, {});
    };
    if (Array.isArray(data)) {
        return data.map((k) => {
            let dataType = getDataType(k);
            return dataType === 'Object' ? filter(k) : k;
        });
    }
    else {
        let dataType = getDataType(data);
        return dataType === 'Object' ? filter(data) : data;
    }
};

const createEvt = ({ target, name, cb, options }) => {
    let el = getEl(target);
    if (!el || !name)
        return;
    let dft = { cancelable: true, composed: false, bubbles: false }, evt = new CustomEvent(name, Object.assign(dft, options)), fn = function (e) {
        cb.call(this, e);
    };
    cb && el.addEventListener(name, fn);
    return {
        event: evt,
        remove: () => {
            cb && el.removeEventListener(name, fn);
        }
    };
};

const optPopup$1 = [
    {
        attr: 'placement',
        prop: 'placement',
        value: 'center',
    },
    {
        attr: 'trigger',
        prop: 'trigger',
        value: 'click',
    },
    {
        attr: 'size',
        prop: 'size',
        value: 'md',
    },
    {
        attr: 'in',
        prop: 'in',
        value: 'fadeIn',
    },
    {
        attr: 'out',
        prop: 'out',
        value: 'fadeOut',
    },
    {
        attr: 'draggable',
        prop: 'draggable',
        value: true,
    },
    {
        attr: 'feature',
        prop: 'feature',
        value: '',
    },
    {
        attr: 'height',
        prop: 'height',
        value: '',
    },
    {
        attr: 'adaptive',
        prop: 'adaptive',
        value: true,
    },
    {
        attr: 'draggable',
        prop: 'draggable',
        value: true,
    },
    {
        attr: 'gesture',
        prop: 'gesture',
        value: {},
    },
    {
        attr: 'parent',
        prop: 'parent',
        value: '',
    },
    {
        attr: 'wing',
        prop: 'wing',
        value: {
            selector: '',
            actClass: '',
        },
    },
    {
        attr: 'mask',
        prop: 'mask',
        value: {
            enable: true,
            closable: true,
        },
    },
    ...optBubble
];

const optGesture = [
    {
        attr: 'parent',
        prop: 'parent',
        value: ''
    },
    {
        attr: 'viewport',
        prop: 'viewport',
        value: {
            enable: false,
            selector: '',
            dmpRatio: 0.8,
            bouncy: true,
            duration: 0,
        },
    },
    {
        attr: 'jitter-click',
        prop: 'jitterClick',
        value: 10,
    },
    {
        attr: 'jitter-trans',
        prop: 'jitterTrans',
        value: 1,
    },
    {
        attr: 'origin',
        prop: 'origin',
        value: null,
    },
    {
        attr: 'unbound',
        prop: 'unbound',
        value: '[unbound]',
    },
    {
        attr: 'spy',
        prop: 'spy',
        value: {
            enable: false,
        },
    },
    {
        attr: 'click',
        prop: 'click',
        value: {
            timeThr: 200,
            delay: 200,
            dblclickable: true,
            holdDelay: 1000,
            hold2Menu: false,
        },
    },
    {
        attr: 'step',
        prop: 'step',
        value: {
            mode: 'translate',
            axis: 'y',
            value: 0,
            duration: 500,
            intvl: 200,
            linkage: true,
            reverse: false,
            curve: 'easeOut',
        },
    },
    {
        attr: 'wheel',
        prop: 'wheel',
        value: false,
    },
    {
        attr: 'keyboard',
        prop: 'keyboard',
        value: {
            enable: false,
            prev: 'ArrowUp',
            next: 'ArrowDown',
            intvl: 200,
            target: null,
        },
    },
    {
        attr: 'translate',
        prop: 'translate',
        value: {
            enable: true,
            max: 10000000,
            min: -10000000,
            instead: false,
            target: '',
        },
    },
    {
        attr: 'drift',
        prop: 'drift',
        value: {
            enable: false,
            timeThr: 300,
            distThr: 20,
            coef: 0.5,
            duration: 500,
            curve: 'easeOut',
            auto: true,
        },
    },
    {
        attr: 'swipe',
        prop: 'swipe',
        value: {
            enable: false,
            timeThr: 100,
            distThr: 20,
            coef: 0.5,
            duration: 500,
            curve: 'easeOut',
            auto: true,
        },
    },
    {
        attr: 'scale',
        prop: 'scale',
        value: {
            enable: false,
            centered: true,
            max: 100,
            min: 0,
        },
    },
    {
        attr: 'rotate',
        prop: 'rotate',
        value: {
            enable: false,
            max: 3600,
            min: -3600,
        },
    },
    {
        attr: 'initial-val',
        prop: 'initialVal',
        value: null,
        
    },
    {
        attr: 'on-start',
        prop: 'onStart',
        value: null,
    },
    {
        attr: 'on-move',
        prop: 'onMove',
        value: null,
    },
    {
        attr: 'on-end',
        prop: 'onEnd',
        value: null,
    },
    {
        attr: 'on-click',
        prop: 'onClick',
        value: null,
    },
    {
        attr: 'on-dblclick',
        prop: 'onDblclick',
        value: null,
    },
    {
        attr: 'on-hold',
        prop: 'onHold',
        value: null,
    },
    {
        attr: 'on-scale',
        prop: 'onScale',
        value: null,
    },
    {
        attr: 'on-scaling',
        prop: 'onScaling',
        value: null,
    },
    {
        attr: 'on-scaled',
        prop: 'onScaled',
        value: null,
    },
    {
        attr: 'on-rotate',
        prop: 'onRotate',
        value: null,
    },
    {
        attr: 'on-rotating',
        prop: 'onRotating',
        value: null,
    },
    {
        attr: 'on-rotated',
        prop: 'onRotated',
        value: null,
    },
    {
        attr: 'on-translate',
        prop: 'onTranslate',
        value: null,
    },
    {
        attr: 'on-translating',
        prop: 'onTranslating',
        value: null,
    },
    {
        attr: 'on-translated',
        prop: 'onTranslated',
        value: null,
    },
    {
        attr: 'on-drift',
        prop: 'onDrift',
        value: null,
    },
    {
        attr: 'on-drifting',
        prop: 'onDrifting',
        value: null,
    },
    {
        attr: 'on-drifted',
        prop: 'onDrifted',
        value: null,
    },
    {
        attr: 'on-canceled',
        prop: 'onCanceled',
        value: null,
    },
    ...optBase
];

const curveVars = {
    linear: getComputedVar(`--${prefix}bez-linear`),
    ease: getComputedVar(`--${prefix}bez-ease`),
    easeOut: getComputedVar(`--${prefix}bez-eo`),
    easeIn: getComputedVar(`--${prefix}bez-ei`),
    easeInOut: getComputedVar(`--${prefix}bez-eio`),
    easeOutIn: getComputedVar(`--${prefix}bez-eoi`),
};

const optSpy = [
    {
        attr: 'margin',
        prop: 'margin',
        value: '0px',
    },
    {
        attr: 'threshold',
        prop: 'threshold',
        value: [0, 1]
    },
    {
        attr: 'root',
        prop: 'root',
        value: null
    },
    {
        attr: 'spy-sel',
        prop: 'spySel',
        value: '',
    },
    {
        attr: 'nav-sel',
        prop: 'navSel',
        value: '',
    },
    {
        attr: 'sub-sel',
        prop: 'subSel',
        value: '',
    },
    {
        attr: 'intvl',
        prop: 'intvl',
        value: 500,
    },
    {
        attr: 'repeat',
        prop: 'repeat',
        value: true,
    },
    {
        attr: 'active',
        prop: 'active',
        value: '',
    },
    {
        attr: 'in',
        prop: 'in',
        value: '',
    },
    {
        attr: 'out',
        prop: 'out',
        value: '',
    },
    {
        attr: 'visible',
        prop: 'visible',
        value: true,
    },
    {
        attr: 'manual',
        prop: 'manual',
        value: false,
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'wing',
        prop: 'wing',
        value: {
            selector: '',
            offset: 0,
            duration: 0,
        },
    },
    {
        attr: 'b4-add',
        prop: 'b4Add',
        value: null,
    },
    {
        attr: 'b4-remove',
        prop: 'b4Remove',
        value: null,
    },
    {
        attr: 'on-show',
        prop: 'onShow',
        value: null,
    },
    {
        attr: 'on-showing',
        prop: 'onShowing',
        value: null,
    },
    {
        attr: 'on-shown',
        prop: 'onShown',
        value: null,
    },
    {
        attr: 'on-in',
        prop: 'onIn',
        value: null,
    },
    {
        attr: 'on-out',
        prop: 'onOut',
        value: null,
    },
    {
        attr: 'on-added',
        prop: 'onAdded',
        value: null,
    },
    {
        attr: 'on-removed',
        prop: 'onRemoved',
        value: null,
    },
    ...optBase
];

class Spy extends ModBaseListen {
    options = {};
    storObs;
    storTmp;
    navEl;
    subEls;
    items;
    interactIns;
    rootEl;
    actIsAttr;
    active;
    hide2Debounce;
    static hostType = 'node';
    static optMaps = optSpy;
    constructor(elem, options = {}) {
        super();
        super.ready({
            options,
            maps: Spy.optMaps,
            host: elem,
            component: false,
        });
        
        
        
        
        
        
        
        this.navEl = this.options.navSel ? getEl(this.options.navSel) : null;
        this.subEls = this.navEl && this.options.subSel ? this.navEl.querySelectorAll(this.options.subSel) : [];
        this.hide2Debounce = debounce((el) => {
            if (el.spy.state === 'hidden') {
                if (el.spy?.out) {
                    classes(el).add(el.spy?.out);
                }
                if (el.spy?.in) {
                    classes(el).remove(el.spy?.in);
                    !this.options.visible && classes(el).add(`${prefix}o`);
                }
                if (this.active) {
                    el.spy.wing.forEach((k) => {
                        this.actIsAttr ? k.removeAttribute(this.active[0]) : classes(k).remove(this.active);
                    });
                }
            }
        }, this.options.intvl);
        this.rootEl = getEl(this.options.root);
        this.getActive();
        super.listen({ name: 'constructed' });
        this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.items = [];
        this.interactIns && this.interactIns.disconnect();
        this.interactIns = new IntersectionObserver(entries => {
            entries.forEach(i => {
                let args = { target: i.target, ratio: i.intersectionRatio, ins: this.interactIns, entry: i };
                if (i.isIntersecting) {
                    args.intersecting = true;
                    if (i.intersectionRatio === 0) {
                        i.target.spy.state = 'show';
                        super.listen({ name: 'shown', params: [args] });
                    }
                    else if (i.intersectionRatio === 1) {
                        i.target.spy.state = 'shown';
                        super.listen({ name: 'shown', params: [args] });
                        this.setActiveOnly(i.target);
                    }
                    else {
                        i.target.spy.state = 'showing';
                        super.listen({ name: 'showing', params: [args] });
                    }
                    if (i.target.spy.ratio < i.intersectionRatio) {
                        super.listen({ name: 'in', params: [args] });
                        !this.options.visible && classes(i.target).remove(`${prefix}o`);
                        if (i.target.spy.state = 'showing') {
                            if (i.target.spy?.in) {
                                classes(i.target).add(i.target.spy?.in, (name) => i.target.classList.contains(name) ? false : true);
                            }
                        }
                        !this.options.repeat && this.interactIns.unobserve(i.target);
                    }
                    i.target.spy.ratio = i.intersectionRatio;
                }
                else {
                    args.intersecting = false;
                    i.target.spy.state = 'hidden';
                    if (this.options.repeat) {
                        this.hide2Debounce.cancel()(i.target);
                    }
                    super.listen({ name: 'out', params: [args] });
                    i.target.spy.ratio = 0;
                }
            });
        }, {
            root: this.rootEl,
            rootMargin: this.options.margin,
            threshold: this.options.threshold,
        });
        if (this.options.spySel) {
            this.items = [...this.targetEl.querySelectorAll(this.options.spySel)];
        }
        else {
            this.targetEl && this.items.push(this.targetEl);
        }
        if (this.options.b4Add) {
            let resp = await this.options.b4Add.call(this, this.items);
            resp && (this.items = resp);
        }
        if (!this.options.manual && this.items.length) {
            this.items.forEach((k) => {
                this.setSpyAttr(k);
                this.interactIns.observe(k);
            });
        }
        super.listen({ name: 'initiated', cb });
        return this;
    }
    getActive() {
        let activeTrim = this.options.active.trim();
        if (activeTrim.startsWith('[') && activeTrim.endsWith(']')) {
            this.actIsAttr = true;
            this.active = activeTrim.replace('[', '').replace(']', '').replace(' ', '').split('=');
        }
        else {
            this.actIsAttr = false;
            this.active = this.options.active;
        }
    }
    setActiveOnly(el) {
        if (!isEmpty(this.active)) {
            el.spy.wing.forEach((k) => {
                if (this.actIsAttr) {
                    if (this.active.length > 0) {
                        k.setAttribute(this.active[0], this.active[1] || '');
                    }
                }
                else {
                    classes(k).add(this.active, (name) => {
                        return k.classList.contains(name) ? false : true;
                    });
                }
            });
            let other = this.items.filter((k) => k !== el && k.spy), otherWings = other.map((k) => k.spy.wing).flat(Infinity);
            otherWings.forEach((k) => {
                this.actIsAttr ? k.removeAttribute(this.active[0]) : classes(k).remove(this.active);
            });
        }
    }
    setSpyAttr(el) {
        if (!getDataType(el).includes('HTML'))
            return;
        let attrs = attrToJson(el, 'spy');
        el.spy = {
            observed: true,
            wing: this.getWings(el),
            in: attrs.in || this.options.in,
            out: attrs.out || this.options.out,
            ratio: 0,
        };
        this.options.classes && classes(el).add(this.options.classes);
        !this.options.visible && classes(el).add(`${prefix}o`);
    }
    getWings(el) {
        let arrFromId = [], arrFromSpy = [], arrFromOpt = [], wingArr = [];
        if (el.id) {
            if (this.subEls.length > 0 && this.subEls[0].hasAttribute('href')) {
                let navWing = this.subEls.find((k) => k.getAttribute('href') === `#${k.id}`);
                navWing && arrFromId.push(navWing);
            }
            else {
                arrFromId = [...document.querySelectorAll(`[href='#${el.id}']`)];
            }
        }
        else {
            let index = this.items.findIndex((k) => k == el);
            index !== -1 && this.subEls[index] ? arrFromId.push(this.subEls[index]) : null;
        }
        if (el.hasAttribute('spy')) {
            let attrs = attrToJson(el, 'spy');
            if (attrs.wing) {
                arrFromSpy = allToEls(attrs.wing);
            }
        }
        if (this.options.wing.selector) {
            arrFromOpt = allToEls(this.options.wing.selector);
        }
        wingArr = [...arrFromId, ...arrFromSpy, ...arrFromOpt].filter(Boolean);
        wingArr.forEach(k => {
            k.onclick = (e) => {
                preventDft(e);
                scrollTo(el, { root: this.rootEl, duration: this.options.wing.duration, offset: this.options.wing.offset });
            };
        });
        return wingArr;
    }
    getEls(data) {
        let dataType = getDataType(data), els = [];
        if (dataType === 'Array') {
            els = data.map((k) => getEl(k)).filter(Boolean);
        }
        else if (dataType === 'String') {
            els = allToEls(data).filter(Boolean);
        }
        return els;
    }
    async add(data, cb) {
        if (this.destroyed)
            return;
        let els = this.getEls(data);
        if (!els.length)
            return this;
        if (this.options.b4Add) {
            let resp = await this.options.b4Add.call(this, els);
            resp && (els = resp);
        }
        els.forEach((i) => {
            if (!i.spy) {
                this.setSpyAttr(i);
                this.interactIns.observe(i);
                !this.items.includes(i) && this.items.push(i);
            }
            else {
                new Message({
                    content: renderTpl(this.options.lang.isObserved, { src: i.dataset.src || i.src }),
                    iconShow: true,
                    status: 'info',
                }).show();
            }
        });
        super.listen({ name: 'add', cb, params: [els] });
        return this;
    }
    async remove(data, cb) {
        if (this.destroyed)
            return;
        let els = this.getEls(data);
        this.options.b4Remove && await this.options.b4Remove.call(this, els);
        els.forEach((i) => {
            if (this.items.includes(i)) {
                if (i.spy) {
                    Reflect.deleteProperty(i, 'spy');
                    this.interactIns.unobserve(i);
                }
                else {
                    new Message({
                        content: renderTpl(this.options.lang.isUnobserved, { src: i.dataset.src || i.src }),
                        iconShow: true,
                        status: 'info',
                    }).show();
                }
            }
        });
        super.listen({ name: 'remove', cb, params: [els] });
        return this;
    }
    
    destroy(cb) {
        if (this.destroyed)
            return;
        this.interactIns.disconnect();
        this.items.forEach((k) => {
            k.spy.wing.forEach((i) => {
                i.onclick = null;
            });
        });
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

const getHypotenuse = (x = 0, y = 0) => Math.sqrt(x * x + y * y);

class Gesture extends ModBaseListen {
    options = {};
    started;
    eventState;
    scrollObj;
    evtTarget;
    isMobi;
    startTime;
    diffTime;
    jitterClick;
    jitterTrans;
    clickCount;
    preventEase;
    holdHandler;
    dragEls;
    canTrans;
    transType;
    stepVal;
    moveVals;
    diffVals;
    startCoord;
    triangleVals;
    moveCount;
    touchesMoveCount;
    paramsFormat;
    params;
    endParams;
    driftListen;
    sizes;
    viewRange;
    viewportEl;
    parentEl;
    initialVals;
    spyIns;
    lastVals;
    nowVals;
    startVals;
    totalTrans;
    startFn;
    moveFn;
    endFn;
    stepFn;
    wheelFn;
    keyboardFn;
    cancelFn;
    menuFn;
    triggerFn;
    transitionendFn;
    preventDft;
    static hostType = 'node';
    static optMaps = optGesture;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            host: elem,
            maps: Gesture.optMaps,
            spread: ['translate', 'drift', 'scale', 'swipe', 'rotate', 'viewport', 'keyboard', 'spy']
        });
        
        
        
        
        
        
        
        
        
        
        
        let _this = this;
        this.started = false;
        this.eventState = 'end';
        this.scrollObj = getScrollObj();
        this.evtTarget = null;
        this.isMobi = isMobi;
        this.startTime = 0;
        this.diffTime = 0;
        this.jitterClick = _this.options.jitterClick + (this.isMobi ? 10 : 0);
        this.jitterTrans = _this.options.jitterTrans + (this.isMobi ? 4 : 0);
        this.clickCount = 0;
        this.preventEase = false;
        this.holdHandler = null;
        this.dragEls = getEls(this.options.translate.target);
        for (let k of this.dragEls)
            k.setAttribute('handle', '');
        this.canTrans = true;
        this.transType = 'translate';
        this.getStepVal();
        this.moveVals = {
            h: 0,
            x: 0,
            y: 0,
            c: { id: 0, x: 0, y: 0 },
            d: { x: '', y: '' }
        };
        this.diffVals = { scale: 0, rotate: 0, translate: { x: 0, y: 0, h: 0 } };
        this.startCoord = { x: 0, y: 0 };
        this.triangleVals = { start: { x: 0, y: 0, h: 0 }, last: { x: 0, y: 0, h: 0 }, now: { x: 0, y: 0, h: 0 } };
        this.moveCount = 0;
        this.touchesMoveCount = 0;
        this.paramsFormat = {
            orgEvt: null,
            pointer: this.isMobi ? 'finger' : 'mouse',
            scale: {
                direction: 0,
                diff: 0,
                value: { x: 1, y: 1 },
                translate: { x: 0, y: 0 }
            },
            rotate: {
                direction: 0,
                diff: 0,
                value: 0
            },
            translate: {
                direction: { x: 0, y: 0 },
                diff: { x: 0, y: 0, h: 0 },
                value: { x: 0, y: 0 },
                canDrift: false,
                canSwipe: false,
            },
            drift: {
                duration: 0,
                direction: { x: 0, y: 0 },
                diff: { x: 0, y: 0, h: 0 },
                value: { x: 0, y: 0 }
            },
            swipe: {
                duration: 0,
                direction: { x: 0, y: 0 },
                diff: { x: 0, y: 0, h: 0 },
                value: { x: 0, y: 0 }
            },
            step: {
                direction: { x: 0, y: 0 },
            },
            coord: { x: 0, y: 0 },
            target: this.targetEl,
            name: '',
            moveStart: false,
            touchesStart: false,
            moveEnd: false,
            moveTime: 0,
        };
        this.params = null;
        this.endParams = {
            moveStart: false,
            touchesStart: false,
            moveEnd: true,
        };
        this.startFn = function (e) {
            _this.evtTarget = getEvtTarget(e);
            _this.prevEvtDft(e);
            _this.params = { ...deepClone(_this.paramsFormat), evtTarget: _this.evtTarget };
            _this.params.orgEvt = e;
            if ((_this.isMobi && e.targetTouches.length > 2 && e.changedTouches.length === 0) || !contains(_this.evtTarget, _this.targetEl)) {
                _this.started = false;
                return;
            }
            _this.getStartVals();
            _this.targetEl.setAttribute('gesture', 'start');
            let changedVals = _this.getTouchCoords(e, 'targetTouches'), targetVals = _this.getTouchCoords(e, 'targetTouches'), paramsHold = { ..._this.params }, paramsTranslate = { ..._this.params }, paramsScale = { ..._this.params }, paramsRotate = { ..._this.params }, paramsStart = { ..._this.params };
            _this.startCoord = _this.getCenterCoord(changedVals);
            _this.startTime = Date.now();
            if (!_this.isMobi || e.targetTouches.length === 1) {
                _this.holdHandler = setTimeout(() => {
                    paramsHold.coord = { ..._this.startCoord };
                    paramsHold.name = 'hold';
                    _this.listen({ name: 'hold', params: [paramsHold] });
                    clearTimeout(_this.holdHandler);
                }, _this.options.click.holdDelay);
            }
            else {
                _this.holdHandler && clearTimeout(_this.holdHandler);
            }
            if (_this.isMobi && e.targetTouches.length > 1) {
                if (_this.options.scale.enable) {
                    _this.startCoord = _this.getCenterCoord(targetVals);
                    paramsScale.coord = { ..._this.startCoord };
                    paramsScale.scale.value = _this.lastVals.scale;
                    paramsScale.name = 'scale';
                    _this.listen({ name: 'scale', params: [paramsScale] });
                }
                if (_this.options.rotate.enable) {
                    paramsRotate.coord = { ..._this.startCoord };
                    paramsRotate.rotate.value = _this.lastVals.rotate;
                    paramsRotate.name = 'rotate';
                    _this.listen({ name: 'rotate', params: [paramsRotate] });
                }
            }
            if (_this.options.translate.enable) {
                paramsTranslate.coord = { ..._this.startCoord };
                paramsTranslate.translate.value = { ..._this.lastVals.translate };
                paramsTranslate.name = 'translate';
                if (_this.dragEls.length === 0 || _this.useHandle(_this.evtTarget)) {
                    _this.listen({ name: 'translate', params: [paramsTranslate] });
                }
                else {
                    _this.canTrans = false;
                }
            }
            paramsStart.coord = { ..._this.startCoord };
            paramsStart.scale.value = _this.lastVals.scale;
            paramsStart.rotate.value = _this.lastVals.rotate;
            paramsStart.translate.value = { ..._this.lastVals.translate };
            paramsStart.totalTrans = _this.totalTrans;
            paramsStart.name = 'start';
            _this.listen({ name: 'start', params: [paramsStart] });
            _this.options.viewport.enable && _this.getSizeDiff(_this.lastVals.translate);
            _this.started = true;
            _this.eventState = 'start';
        };
        this.moveFn = function (e) {
            _this.evtTarget = getEvtTarget(e);
            if (!_this.started)
                return;
            _this.prevEvtDft(e);
            
            _this.driftListen && cancelAnimationFrame(_this.driftListen.frame);
            _this.diffTime = Date.now() - _this.startTime;
            _this.moveCount++;
            _this.params.orgEvt = e;
            let targetVals = _this.getTouchCoords(e, 'targetTouches'), moveStart = _this.moveCount === 1 ? true : false;
            _this.moveVals = _this.getMoveVals(targetVals, _this.startCoord);
            if (_this.moveVals.h <= _this.jitterTrans) {
                return;
            }
            else {
                _this.targetEl.style.transitionDuration = `0ms`;
            }
            let paramsTouches = { ..._this.params, coord: _this.moveVals.c, moveStart, evtTarget: _this.evtTarget }, paramsTranslate = { ...paramsTouches }, paramsScale = { ...paramsTouches }, paramsRotate = { ...paramsTouches }, paramsMove = { ...paramsTouches };
            if (_this.options.translate.enable) {
                _this.diffVals.translate = { x: ~~_this.moveVals.x, y: ~~_this.moveVals.y, h: ~~_this.moveVals.h };
                _this.updateNowTrans();
                _this.options.viewport.enable && (_this.nowVals.translate = _this.getBounceVals(_this.nowVals.translate));
                _this.moveVals.h > _this.jitterClick && _this.holdHandler && clearTimeout(_this.holdHandler);
                paramsTranslate = extend({
                    target: paramsTouches,
                    source: {
                        translate: {
                            diff: { ..._this.diffVals.translate },
                            value: { ..._this.nowVals.translate },
                            direction: _this.moveVals.d
                        },
                        name: 'translating'
                    }
                });
                _this.totalTrans = _this.nowVals.translate;
                if (_this.parentEl && isOutside(e, _this.parentEl))
                    _this.canTrans = false;
                if (_this.canTrans) {
                    if (_this.options.swipe.enable && _this.diffTime < _this.options.swipe.timeThr && _this.diffVals.translate.h > _this.options.swipe.distThr) {
                        _this.transType = 'swipe';
                    }
                    else {
                        _this.transType = 'translate';
                        _this.targetEl.setAttribute('gesture', 'move');
                        _this.listen({ name: 'translating', params: [paramsTranslate] });
                    }
                }
            }
            if (_this.isMobi && e.targetTouches.length > 1) {
                _this.touchesMoveCount++;
                _this.triangleVals.now = _this.getTriangleVals(targetVals[0], targetVals[1]);
                if (_this.touchesMoveCount === 1) {
                    _this.triangleVals.start = { ..._this.triangleVals.now };
                    _this.diffVals.scale = 0;
                    _this.diffVals.rotate = 0;
                    paramsTouches.touchesStart = true;
                }
                else {
                    _this.diffVals.scale = (_this.triangleVals.now.h / _this.triangleVals.last.h) - 1;
                    _this.diffVals.rotate = _this.triangleVals.now.a - _this.triangleVals.last.a;
                    _this.correctDiffRotate();
                }
                _this.correctRangeScale();
                _this.correctRangeRotate();
                if (_this.options.scale.enable) {
                    paramsScale = extend({
                        target: paramsTouches,
                        source: {
                            scale: {
                                diff: toNumber(_this.diffVals.scale),
                                value: { x: toNumber(_this.nowVals.scale.x), y: toNumber(_this.nowVals.scale.y) },
                                direction: _this.diffVals.scale > 0 ? 1 : _this.diffVals.scale < 0 ? -1 : 0
                            },
                            name: 'scaling'
                        }
                    });
                    if (!_this.options.scale.centered) {
                        paramsScale.scale.translate = _this.getScaleTrans({
                            coord: paramsScale.coord,
                            diff: paramsScale.scale.diff,
                        });
                        _this.totalTrans = _this.getScaleTrans({
                            initVal: _this.totalTrans,
                            coord: paramsScale.coord,
                            diff: paramsScale.scale.diff,
                        });
                    }
                    _this.listen({ name: 'scaling', params: [paramsScale] });
                }
                if (_this.options.rotate.enable) {
                    paramsRotate = extend({
                        target: paramsTouches,
                        source: {
                            rotate: {
                                diff: toNumber(_this.diffVals.rotate),
                                value: toNumber(_this.nowVals.rotate),
                                direction: _this.diffVals.rotate > 0 ? 1 : _this.diffVals.rotate < 0 ? -1 : 0
                            },
                            name: 'rotating'
                        }
                    });
                    _this.listen({ name: 'rotating', params: [paramsRotate] });
                }
            }
            paramsMove = extend({
                target: paramsTouches,
                source: {
                    rotate: paramsRotate.rotate,
                    scale: paramsScale.scale,
                    translate: paramsTranslate.translate,
                    totalTrans: _this.totalTrans,
                    name: 'move'
                }
            });
            _this.listen({ name: 'move', params: [paramsMove] });
            _this.triangleVals.last = { ..._this.triangleVals.now };
            _this.eventState = 'move';
        };
        this.endFn = function (e) {
            if (!_this.started)
                return;
            _this.prevEvtDft(e);
            _this.moveCount = _this.touchesMoveCount = 0;
            _this.params.orgEvt = e;
            _this.params.evtTarget = _this.evtTarget;
            _this.params.moveTime = _this.diffTime = Date.now() - _this.startTime;
            _this.holdHandler && clearTimeout(_this.holdHandler);
            let paramsTranslate = {
                ..._this.params,
                coord: { ..._this.moveVals.c },
                translate: {
                    diff: { ..._this.diffVals.translate },
                    value: { ..._this.nowVals.translate },
                    direction: { ..._this.moveVals.d },
                },
                ..._this.endParams,
            }, paramsClick = { ..._this.params, ..._this.endParams }, paramsScale = { ...paramsClick }, paramsRotate = { ...paramsClick }, paramsEnd = { ...paramsClick };
            if (_this.diffVals.translate.h < _this.jitterClick) {
                if (_this.diffTime < _this.options.click.timeThr && (!_this.isMobi && e.button === 0 || _this.isMobi)) {
                    let coords = _this.getTouchCoords(e, 'changedTouches'), endCoord = _this.getCenterCoord(coords);
                    paramsClick.coord = endCoord;
                    paramsClick.name = 'click';
                    if (_this.options.click.dblclickable) {
                        _this.clickCount++;
                        if (_this.clickCount === 1) {
                            setTimeout(() => {
                                if (_this.clickCount === 1) {
                                    _this.listen({ name: 'click', params: [paramsClick] });
                                }
                                else if (_this.clickCount > 1) {
                                    paramsClick.name = 'dblclick';
                                    _this.listen({ name: 'dblclick', params: [paramsClick] });
                                }
                                _this.clickCount = 0;
                            }, _this.options.click.delay);
                        }
                    }
                    else {
                        _this.listen({ name: 'click', params: [paramsClick] });
                    }
                }
            }
            if (_this.canTrans && _this.options.translate.enable) {
                let canDrift = _this.diffVals.translate.h > (_this.options.drift.distThr * (_this.isMobi ? 2 : 1)) && _this.diffTime < (_this.options.drift.timeThr / (_this.isMobi ? 2 : 1));
                paramsTranslate.translate.canSwipe = _this.transType === 'swipe' ? true : false;
                paramsTranslate.translate.canDrift = paramsTranslate.translate.canSwipe ? false : canDrift;
                paramsTranslate.name = 'translated';
                _this.listen({ name: 'translated', params: [paramsTranslate] });
                if (!_this.preventEase) {
                    if (_this.transType === 'swipe') {
                        _this.updateEaseParamsListen(paramsTranslate, 'swipe');
                    }
                    else {
                        (_this.options.drift.enable && canDrift) && _this.updateEaseParamsListen(paramsTranslate);
                    }
                }
                _this.options.viewport.enable && _this.moveVals.h > 1 && _this.rebound();
            }
            if (_this.isMobi) {
                if (_this.options.scale.enable) {
                    paramsScale.scale.diff = toNumber(_this.diffVals.scale);
                    paramsScale.scale.value = { x: toNumber(_this.nowVals.scale.x), y: toNumber(_this.nowVals.scale.y) };
                    paramsScale.name = 'scaled';
                    _this.listen({ name: 'scaled', params: [paramsScale] });
                }
                if (_this.options.rotate.enable) {
                    paramsRotate.rotate.diff = toNumber(_this.diffVals.rotate);
                    paramsRotate.rotate.value = toNumber(_this.nowVals.rotate);
                    paramsRotate.name = 'rotated';
                    _this.listen({ name: 'rotated', params: [paramsRotate] });
                }
                _this.options.viewport.enable && _this.moveVals.h > 1 && _this.getSizeDiff();
                _this.lastVals.rotate = _this.nowVals.rotate;
                _this.lastVals.scale = { ..._this.nowVals.scale };
            }
            paramsEnd.translate = paramsTranslate.translate;
            paramsEnd.drift = paramsTranslate.drift;
            paramsEnd.scale = paramsScale.scale;
            paramsEnd.rotate = paramsRotate.rotate;
            paramsEnd.totalTrans = _this.totalTrans;
            paramsEnd.name = 'end';
            _this.listen({ name: 'end', params: [paramsEnd] });
            _this.lastVals.translate = { ..._this.nowVals.translate };
            _this.setCompleted();
            _this.removeEvents();
        };
        this.stepFn = (e, dirValue, type = 'wheel') => {
            if (!_this.options.scale.enable && !_this.options.rotate.enable && !_this.options.translate.enable)
                return;
            _this.options.viewport.enable && _this.getSizeDiff();
            _this.getStartVals();
            _this.options.step.duration ? _this.targetEl.style.transitionDuration = `${_this.options.step.duration}ms` : null;
            _this.targetEl.style.transitionTimingFunction = curveVars[_this.options.step.curve];
            _this.params = { ...deepClone(_this.paramsFormat), evtTarget: (type === 'keyboard' ? this.targetEl : getEvtTarget(e)) };
            _this.params = extend({
                target: _this.params,
                source: {
                    pointer: type,
                    orgEvt: e,
                    coord: type === 'keyboard' ? { x: 0, y: 0 } : { x: e.clientX, y: e.clientY },
                    step: {
                        direction: { x: dirValue, y: dirValue }
                    },
                    translate: { value: { ..._this.nowVals.translate } },
                    scale: {
                        value: { ..._this.nowVals.scale },
                        direction: dirValue
                    },
                    rotate: {
                        value: _this.nowVals.rotate,
                        direction: dirValue
                    },
                    moveTime: parseFloat(style(_this.targetEl).transitionDuration) * 1000
                }
            });
            _this.listen({ name: 'step', params: [{ ..._this.params, name: 'step' }] });
            let paramsAction = { ..._this.params };
            if (_this.options.step.mode === 'scale') {
                if (!_this.options.scale.enable)
                    return;
                _this.diffVals.scale = (dirValue === 1 ? _this.stepVal : -_this.stepVal);
                _this.correctRangeScale();
                paramsAction.scale.diff = toNumber(_this.diffVals.scale);
                paramsAction.scale.value = { x: toNumber(_this.nowVals.scale.x), y: toNumber(_this.nowVals.scale.y) };
                if (!_this.options.scale.centered) {
                    paramsAction.scale.translate = _this.getScaleTrans({
                        coord: paramsAction.coord,
                        diff: paramsAction.scale.diff,
                    });
                    _this.totalTrans = _this.getScaleTrans({
                        initVal: _this.totalTrans,
                        coord: paramsAction.coord,
                        diff: paramsAction.scale.diff,
                    });
                }
                _this.options.step.linkage && _this.listen({ name: 'scaling', params: [{ ...paramsAction, name: 'scaling' }] });
            }
            else if (_this.options.step.mode === 'rotate') {
                if (!_this.options.rotate.enable)
                    return;
                _this.diffVals.rotate = (dirValue === 1 ? _this.stepVal : -_this.stepVal);
                _this.correctRangeRotate();
                paramsAction.rotate.diff = toNumber(_this.diffVals.rotate);
                paramsAction.rotate.value = toNumber(_this.nowVals.rotate);
                _this.options.step.linkage && _this.listen({ name: 'rotating', params: [{ ...paramsAction, name: 'rotating' }] });
            }
            else if (_this.options.step.mode === 'translate') {
                if (!_this.options.translate.enable)
                    return;
                let direction = _this.options.step.axis === 'y' ? { x: 0, y: -dirValue, } : { x: -dirValue, y: 0 }, axisDist = -dirValue * _this.stepVal, x = _this.options.step.axis === 'y' ? 0 : axisDist, y = _this.options.step.axis === 'x' ? 0 : axisDist;
                _this.diffVals.translate = { x, y, h: _this.stepVal };
                _this.updateNowTrans();
                _this.options.viewport.enable && (_this.nowVals.translate = _this.getViewLimitVals(_this.nowVals.translate));
                paramsAction.translate.diff = _this.diffVals.translate;
                paramsAction.translate.value = _this.nowVals.translate;
                paramsAction.translate.direction = direction;
                _this.options.step.linkage && _this.listen({ name: 'translating', params: [{ ...paramsAction, name: 'translating' }] });
            }
            _this.listen({ name: 'stepped', params: [{ ...paramsAction, name: 'stepped' }] });
            _this.listen({ name: 'move', params: [{ ...paramsAction, totalTrans: _this.totalTrans, name: 'move' }] });
            _this.lastVals[_this.options.step.mode] = { ..._this.nowVals[_this.options.step.mode] };
        };
        this.wheelFn = throttle(function (e) {
            e.stopPropagation();
            e.preventDefault();
            let isUp = _this.correctWheelDirection(isScrollUp(e, _this.scrollObj.detail)), dirValue = isUp ? 1 : -1;
            _this.stepFn(e, dirValue, 'wheel');
        }, { intvl: _this.options.step.intvl, prevent: true });
        this.keyboardFn = throttle((e) => {
            e.stopPropagation();
            e.preventDefault();
            let dirValue = (e.code === this.options.keyboard.prev) ? -1 : (e.code === this.options.keyboard.next) ? 1 : 0;
            if (!dirValue)
                return;
            _this.stepFn(e, dirValue, 'keyboard');
        }, { intvl: _this.options.step.intvl, prevent: true });
        this.cancelFn = function (e) {
            _this.setCompleted();
            _this.removeEvents();
            let paramsCancel = Object.assign(deepClone(_this.paramsFormat), {
                orgEvt: e,
                evtTarget: getEvtTarget(e),
                coord: { x: e.clientX, y: e.clientY },
                ..._this.endParams,
                name: 'canceled'
            });
            _this.listen({ name: 'canceled', params: [paramsCancel] });
        };
        this.menuFn = function (e) {
            if (_this.isUnbound(e.target))
                return;
            e.stopPropagation();
            e.preventDefault();
            let paramsClick = Object.assign(deepClone(_this.paramsFormat), {
                orgEvt: e,
                evtTarget: getEvtTarget(e),
                coord: { x: e.clientX, y: e.clientY },
                ..._this.endParams,
                name: 'hold'
            });
            _this.listen({ name: 'hold', params: [paramsClick] });
        };
        this.triggerFn = (e) => {
            if (this.isUnbound(getEvtTarget(e)))
                return;
            e.stopPropagation();
            this.addEvents();
        };
        this.transitionendFn = (e) => {
            e.stopPropagation();
            if (this.options.viewport.enable) {
                this.getSizeDiff(this.nowVals.translate);
                this.rebound(this.nowVals.translate);
            }
        };
        this.preventDft = (e) => {
            e?.preventDefault();
        };
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    getStartVals() {
        this.startVals = transformTools.get(this.targetEl, ['translate', 'scale', 'rotate'], this.options.translate.instead);
        this.totalTrans = { ...this.startVals.translate };
        this.nowVals = {
            translate: {
                ...this.startVals.translate,
                h: ~~getHypotenuse(this.startVals.translate.x, this.startVals.translate.y)
            },
            scale: { ...this.startVals.scale },
            rotate: this.startVals.rotate,
        };
        this.lastVals = deepClone(this.nowVals);
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch (err) {
            err ? console.error(err) : console.warn(config.warn.init);
            return this;
        }
        this.setEmpty();
        this.parentEl = getEl(this.options.parent);
        this.viewportEl = getEl(this.options.viewport.selector);
        this.options.initialVal ? transformTools.set({ el: this.targetEl, data: this.options.initialVal, instead: this.options.translate.instead }) : null;
        this.initialVals = this.options.translate.instead ?
            transformTools.get(this.targetEl, ['translate', 'scale', 'rotate', 'skew'], this.options.translate.instead) :
            this.targetEl.style.transform;
        this.targetEl.addEventListener('pointerdown', this.triggerFn, { passive: false });
        this.targetEl.removeEventListener('pointerup', this.triggerFn);
        if (this.isMobi) {
            if (this.dragEls.length) {
                for (let k of this.dragEls)
                    k.addEventListener('touchstart', this.preventDft, { passive: false });
            }
            else {
                this.targetEl.addEventListener('touchstart', this.preventDft, { passive: false });
            }
        }
        this.targetEl.addEventListener("transitionend", this.transitionendFn, { passive: false });
        if (!this.isMobi) {
            if (this.options.wheel) {
                this.targetEl.removeEventListener(this.scrollObj.event, this.wheelFn);
                this.targetEl.addEventListener(this.scrollObj.event, this.wheelFn, { passive: false });
            }
            if (this.options.click.hold2Menu) {
                this.targetEl.removeEventListener('contextmenu', this.menuFn);
                this.targetEl.addEventListener('contextmenu', this.menuFn);
            }
        }
        this.setSpy();
        this.setCompleted();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    setEmpty() {
    }
    useHandle(target) {
        return this.dragEls.length && (this.dragEls.find((k) => contains(target, k)));
    }
    prevEvtDft(evt, target = this.evtTarget) {
        if (this.isMobi) {
            if (!this.options.scale.enable && !this.options.rotate.enable) {
                if (this.useHandle(target) || (!this.dragEls.length && contains(target, this.targetEl))) {
                    preventDft(evt, true);
                }
            }
            else {
                contains(target, this.targetEl) && preventDft(evt, true);
            }
        }
        else {
            if (this.useHandle(target) || (!this.dragEls.length && contains(target, this.targetEl))) {
                preventDft(evt, true);
            }
        }
    }
    setSpy() {
        if (!this.options.spy.enable)
            return;
        let opts = extend({
            target: {
                onIn: () => {
                    super.listen({ name: 'showing' });
                },
                onOut: () => {
                    this.options.keyboard.enable && !this.isMobi && window.removeEventListener('keydown', this.keyboardFn);
                    super.listen({ name: 'hidden' });
                },
                onShown: () => {
                    this.options.keyboard.enable && !this.isMobi && window.addEventListener('keydown', this.keyboardFn, { passive: false });
                    super.listen({ name: 'shown' });
                }
            },
            source: this.options.spy,
        });
        this.spyIns = new Spy(getEl(this.options.keyboard.target) || this.targetEl, opts);
    }
    isUnbound(target) {
        let unbounds = getEls(this.options.unbound, this.targetEl);
        return unbounds.find((k) => contains(target, k));
    }
    updateEaseParamsListen(params, type = 'drift') {
        let distH = ~~((this.options[type].timeThr - this.diffTime) * this.options[type].coef) * (type === 'swipe' ? (this.isMobi ? 8 : 12) : 1), distX = ~~(distH * (this.diffVals.translate.x / this.diffVals.translate.h)), distY = ~~(distH * (this.diffVals.translate.y / this.diffVals.translate.h)), driftX = this.clampVals('translate', params.translate.value.x + distX), driftY = this.clampVals('translate', params.translate.value.y + distY);
        params[type].direction = params.translate.direction;
        params[type].diff = { x: distX, y: distY, h: distH };
        params[type].duration = this.options[type].duration;
        params[type].value = this.options.viewport.enable ? this.getViewLimitVals({ x: driftX, y: driftY }) : { x: driftX, y: driftY };
        params.name = type;
        this.listen({ name: type, params: [params] });
        if (this.options[type].auto) {
            this.targetEl.style.transitionDuration = `0ms`;
            this.driftListen = ease({
                from: params.translate.value,
                to: params[type].value,
                curve: this.options[type].curve,
                duration: this.options[type].duration,
                doing: (e) => {
                    params.translate.value = { x: Math.floor(e.value.x), y: Math.floor(e.value.y) };
                    this.listen({ name: (type === 'swipe' ? 'swiping' : 'drifting'), params: [params] });
                }, done: () => {
                    let translateVals = transformTools.get(this.targetEl, ['translate'], this.options.translate.instead).translate;
                    if ((params[type].value.x === translateVals.x) && (params[type].value.y === translateVals.y)) {
                        this.listen({ name: (type === 'swipe' ? 'swiped' : 'drifted'), params: [params] });
                    }
                }
            });
        }
    }
    updateNowTrans() {
        let x = this.lastVals.translate.x + this.diffVals.translate.x, y = this.lastVals.translate.y + this.diffVals.translate.y;
        this.nowVals.translate = { x: ~~x, y: ~~y };
        this.correctRangeTrans();
    }
    getMoveVals(vals, startCoord) {
        let c = this.getCenterCoord(vals), diff = this.getTriangleVals(c, startCoord), h = ~~diff.h, x = ~~diff.x, y = ~~diff.y, d = { x: x > 0 ? 1 : x < 0 ? -1 : 0, y: y > 0 ? 1 : y < 0 ? -1 : 0 };
        return { c, h, x, y, d };
    }
    removeEvents() {
        document.removeEventListener(eventMap[0], this.startFn);
        document.removeEventListener(eventMap[1], this.moveFn);
        document.removeEventListener(eventMap[2], this.endFn);
        document.removeEventListener(eventMap[3], this.cancelFn);
    }
    addEvents() {
        document.addEventListener(eventMap[0], this.startFn, { passive: false });
        document.addEventListener(eventMap[1], this.moveFn, { passive: false });
        document.addEventListener(eventMap[2], this.endFn, { passive: false });
        document.addEventListener(eventMap[3], this.cancelFn, { passive: false });
    }
    setCompleted() {
        this.started = false;
        this.moveCount = this.touchesMoveCount = 0;
        this.diffVals = { scale: 0, rotate: 0, translate: { x: 0, y: 0, h: 0 } };
        this.holdHandler && clearTimeout(this.holdHandler);
        this.canTrans = true;
        this.preventEase = false;
        this.targetEl.style.cssText = this.targetEl.style.cssText.replace('transition-duration: 0ms', '');
        this.targetEl.removeAttribute('gesture');
    }
    getOrign(coord, rect) {
        let x = coord.x - rect.left, y = coord.y - rect.top;
        x < 0 ? x = 0 : x > rect.width ? x = rect.width : null;
        y < 0 ? y = 0 : y > rect.height ? y = rect.height : null;
        return { x, y };
    }
    getScaleTrans(options) {
        let styles = style(this.targetEl), width = parseInt(styles.width), height = parseInt(styles.height), origin = this.options.origin || { x: width / 2, y: height / 2 }, rect = this.targetEl.getBoundingClientRect(), newOrigin = this.getOrign(options.coord, rect), transform = transformTools.get(this.targetEl, ['translate'], this.options.translate.instead), percent = (attr) => {
            let value = newOrigin[attr] / rect[attr === 'x' ? 'width' : 'height'];
            value < 0 ? value = 0 : value > 1 ? value = 1 : null;
            return value;
        }, orginDiff = (attr) => {
            return (origin[attr] - (attr === 'x' ? width : height) * percent(attr)) * options.diff;
        }, initVal = options.initVal || { x: transform.translate.x, y: transform.translate.y }, x = initVal.x + orginDiff('x'), y = initVal.y + orginDiff('y');
        return { x: ~~x, y: ~~y };
    }
    getTouchCoords(event, target = 'targetTouches') {
        let touches = [];
        if (this.isMobi) {
            for (let k of event[target]) {
                touches.push({
                    x: ~~k.clientX,
                    y: ~~k.clientY
                });
            }
        }
        else {
            touches.push({
                x: ~~event.clientX,
                y: ~~event.clientY
            });
        }
        return touches;
    }
    getCenterCoord(data) {
        let result, avg = (n, axis) => {
            let value = 0;
            for (let k = 0; k < n; k++) {
                value += data[k][axis];
            }
            return value / n;
        };
        if (data.length > 1) {
            result = { x: ~~avg(2, 'x'), y: ~~avg(2, 'y') };
        }
        else if (data.length === 1) {
            result = data[0];
        }
        else {
            result = { x: 0, y: 0 };
        }
        return result;
    }
    getTriangleVals(now, last) {
        let x = now.x - last.x, y = now.y - last.y, h = getHypotenuse(x, y), a = Math.atan2(y, x) * 180 / Math.PI;
        return { x, y, h, a };
    }
    correctRangeTrans() {
        let x = this.clampVals('translate', this.nowVals.translate.x), y = this.clampVals('translate', this.nowVals.translate.y), tmpX, tmpY, diffX, diffY, diffH;
        if (x !== this.nowVals.translate.x) {
            tmpX = x;
            diffX = x - this.lastVals.translate.x;
        }
        else {
            tmpX = this.nowVals.translate.x;
            diffX = this.diffVals.translate.x;
        }
        if (y !== this.nowVals.translate.y) {
            tmpY = y;
            diffY = y - this.lastVals.translate.y;
        }
        else {
            tmpY = this.nowVals.translate.y;
            diffY = this.diffVals.translate.y;
        }
        diffH = getHypotenuse(diffX, diffY);
        this.diffVals.translate = { x: diffX, y: diffY, h: diffH };
        this.nowVals.translate = { x: tmpX, y: tmpY };
    }
    correctRangeRotate() {
        this.nowVals.rotate = this.clampVals('rotate', this.nowVals.rotate);
        let tmp = this.nowVals.rotate + this.diffVals.rotate;
        if (tmp >= this.options.rotate.max) {
            this.diffVals.rotate = this.options.rotate.max - this.nowVals.rotate;
        }
        else if (tmp <= this.options.rotate.min) {
            this.diffVals.rotate = this.options.rotate.min - this.nowVals.rotate;
        }
        this.nowVals.rotate += this.diffVals.rotate;
    }
    correctDiffRotate() {
        if (this.triangleVals.now.a > 0 && this.triangleVals.last.a < 0) {
            if (this.triangleVals.now.a > 90 && this.triangleVals.last.a < -90) {
                this.diffVals.rotate = 360 - this.diffVals.rotate;
            }
            else if (this.triangleVals.now.a < 90 && this.triangleVals.last.a > -90) {
                this.diffVals.rotate = -this.diffVals.rotate;
            }
        }
        else if (this.triangleVals.now.a < 0 && this.triangleVals.last.a > 0) {
            if (this.triangleVals.now.a > -90 && this.triangleVals.last.a < 90) {
                this.diffVals.rotate = -this.diffVals.rotate;
            }
            else if (this.triangleVals.now.a < -90 && this.triangleVals.last.a > 90) {
                this.diffVals.rotate = -(360 + this.diffVals.rotate);
            }
        }
    }
    correctRangeScale() {
        this.nowVals.scale.x = this.clampVals('scale', this.nowVals.scale.x);
        this.nowVals.scale.y = this.clampVals('scale', this.nowVals.scale.y);
        let tmpX = this.nowVals.scale.x + this.diffVals.scale, tmpY = this.nowVals.scale.y + this.diffVals.scale;
        if (tmpX >= this.options.scale.max || tmpY >= this.options.scale.max) {
            this.diffVals.scale = Math.min((this.options.scale.max - this.nowVals.scale.x), (this.options.scale.max - this.nowVals.scale.y));
        }
        else if (tmpX <= this.options.scale.min || tmpY <= this.options.scale.min) {
            this.diffVals.scale = Math.min((this.options.scale.min - this.nowVals.scale.x), (this.options.scale.min - this.nowVals.scale.y));
        }
        this.nowVals.scale.x += this.diffVals.scale;
        this.nowVals.scale.y += this.diffVals.scale;
    }
    clampVals(prop, value) {
        return clampVal({ val: value, min: this.options[prop].min, max: this.options[prop].max });
    }
    correctWheelDirection(value) {
        return this.options.step.reverse ? !value : value;
    }
    getStepVal() {
        if (!this.options.step.value) {
            this.stepVal = this.options.step.mode === 'translate' ? 60 : this.options.step.mode === 'scale' ? 0.5 : this.options.step.mode === 'rotate' ? 10 : 0;
        }
        else {
            this.stepVal = this.options.step.value = 0;
        }
    }
    getSizeDiff(val) {
        this.sizes = {
            viewport: { width: 0, height: 0, left: 0, right: 0, top: 0, bottom: 0 },
            target: { width: 0, height: 0, left: 0, right: 0, top: 0, bottom: 0 },
            diff: { x: 0, y: 0 },
            offset: { left: 0, right: 0, top: 0, bottom: 0 }
        };
        if (!this.viewportEl || this.viewportEl === document.body || this.viewportEl === document.documentElement) {
            this.sizes.viewport = {
                width: document.documentElement.clientWidth,
                height: document.documentElement.clientHeight,
                left: 0,
                top: 0,
                right: document.documentElement.clientWidth,
                bottom: document.documentElement.clientHeight
            };
        }
        else {
            let tmp = this.viewportEl.getBoundingClientRect();
            this.sizes.viewport = {
                width: tmp.width,
                height: tmp.height,
                left: tmp.left,
                top: tmp.top,
                right: tmp.right,
                bottom: tmp.bottom,
            };
        }
        let rect = this.targetEl.getBoundingClientRect();
        this.sizes.target = {
            width: rect.width,
            height: rect.height,
            left: rect.left,
            top: rect.top,
            right: rect.right,
            bottom: rect.bottom,
        };
        this.sizes.diff = { x: this.sizes.viewport.width - this.sizes.target.width, y: this.sizes.viewport.height - this.sizes.target.height };
        this.sizes.offset = {
            left: this.sizes.viewport.left - this.sizes.target.left,
            right: this.sizes.viewport.right - this.sizes.target.right,
            top: this.sizes.viewport.top - this.sizes.target.top,
            bottom: this.sizes.viewport.bottom - this.sizes.target.bottom,
        };
        
        this.getViewRange(val);
    }
    getViewRange(val) {
        let value = val || transformTools.get(this.targetEl, ['translate'])['translate'];
        this.viewRange = {
            x: [value.x + this.sizes.offset.left, value.x + this.sizes.offset.right].sort(),
            y: [value.y + this.sizes.offset.top, value.y + this.sizes.offset.bottom].sort(),
        };
    }
    getDmpVals(value) {
        let dmp = this.options.viewport.dmpRatio ** 2;
        return this.options.viewport.bouncy ? value * dmp : value;
    }
    getBounceVals(data) {
        let x = data.x, y = data.y;
        if (x < this.viewRange.x[0]) {
            x += this.getDmpVals(this.viewRange.x[0] - x);
        }
        else if (x > this.viewRange.x[1]) {
            x += this.getDmpVals(this.viewRange.x[1] - x);
        }
        if (y < this.viewRange.y[0]) {
            y += this.getDmpVals(this.viewRange.y[0] - y);
        }
        else if (y > this.viewRange.y[1]) {
            y += this.getDmpVals(this.viewRange.y[1] - y);
        }
        return { x: ~~x, y: ~~y };
    }
    getViewLimitVals(data) {
        let x = clampVal({ val: data.x, min: this.viewRange.x[0], max: this.viewRange.x[1] }), y = clampVal({ val: data.y, min: this.viewRange.y[0], max: this.viewRange.y[1] });
        return { x, y };
    }
    getAutoDur(val1, val2) {
        return Math.abs(val1 - val2) / 3 + 200;
    }
    rebound(val) {
        let tmp = val || this.nowVals.translate, dur;
        if (tmp.x < this.viewRange.x[0]) {
            dur = this.getAutoDur(this.viewRange.x[0], tmp.x);
            tmp.x = this.viewRange.x[0];
        }
        else if (tmp.x > this.viewRange.x[1]) {
            dur = this.getAutoDur(this.viewRange.x[1], tmp.x);
            tmp.x = this.viewRange.x[1];
        }
        if (tmp.y < this.viewRange.y[0]) {
            dur = this.getAutoDur(this.viewRange.y[0], tmp.y);
            tmp.y = this.viewRange.y[0];
        }
        else if (tmp.y > this.viewRange.y[1]) {
            dur = this.getAutoDur(this.viewRange.y[1], tmp.y);
            tmp.y = this.viewRange.y[1];
        }
        this.targetEl.style.transitionDuration = `${this.options.viewport.duration || dur}ms`;
        transformTools.set({
            el: this.targetEl,
            data: { translate: { x: tmp.x, y: tmp.y } }
        });
    }
    reset(cb) {
        this.targetEl.style.transform = this.initialVals;
        super.listen({ name: 'reset', cb });
        return this;
    }
    destroy(cb) {
        if (this.destroyed) {
            return this;
        }
        this.setCompleted();
        this.targetEl.removeEventListener('pointerdown', this.triggerFn);
        this.targetEl.removeEventListener("transitionend", this.transitionendFn);
        if (this.isMobi) {
            if (this.dragEls.length) {
                for (let k of this.dragEls)
                    k.removeEventListener('touchstart', this.preventDft);
            }
            else {
                this.targetEl.removeEventListener('touchstart', this.preventDft);
            }
        }
        this.removeEvents();
        if (!this.isMobi) {
            this.options.wheel && this.targetEl.removeEventListener(this.scrollObj.event, this.wheelFn);
            this.options.keyboard.enable && window.removeEventListener('keydown', this.keyboardFn);
            this.options.click.hold2Menu && this.targetEl.removeEventListener('contextmenu', this.menuFn);
            this.spyIns && this.spyIns.destroy();
        }
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

class Dialog extends ModBaseListenCacheBubble {
    options = {};
    timestamp;
    bubbleType;
    parentEl;
    lastPlace;
    lastSize;
    lastPlacement;
    gestureIns;
    wrapSize;
    confirmEl;
    cancelEl;
    closeEl;
    triggerShow;
    content;
    duration;
    wrapHeight;
    aniIn;
    aniOut;
    addPulseAnim;
    removePulseAnim;
    static optMaps = optPopup$1;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            host: elem,
            maps: Dialog.optMaps,
            component: true,
            spread: ['mask', 'tools', 'padding', 'footer', 'autoFill', 'bullet']
        });
        
        
        
        
        
        
        
        
        
        
        
        
        this.bubbleType = 'dialog';
        this.timestamp = Date.now();
        let _this = this;
        this.triggerShow = function (evt) {
            if (this === _this.targetEl && _this.options.canClick) {
                let target = getEvtTarget(evt), tmp = _this.options.canClick.call(_this, target, evt);
                if (!tmp)
                    return;
            }
            if (_this.state === 'hidden') {
                _this.show();
                (_this.options.wing.actClass && _this.wings.includes(this)) && this.classList.add(_this.options.wing.actClass);
            }
            else if (_this.state === 'shown') {
                !_this.options.keepShow && _this.hide();
            }
        };
        this.triggerClose = () => {
            !this.options.keepShow && this.hide();
        };
        this.addPulseAnim = () => {
            this.mainEl.classList.add(`${prefix}pulseShow`);
        };
        this.removePulseAnim = () => {
            this.mainEl.classList.remove(`${prefix}pulseShow`);
        };
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.setEmpty();
        this.parentEl = getEl(this.options.parent) || document.body;
        this.options.placement = this.options.placement || 'center';
        this.setFeature();
        
        this.wings = getEls(this.options.wing.selector);
        this.renderBubble('dialog');
        this.setAttrs();
        await getContent.call(this, {
            content: this.options.content,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: {
                xhrName: 'contXhr',
                spinSel: this.contEl,
                ...this.options.ajax
            },
            request: (data) => {
                this.listen({ name: 'request', params: [data] });
            },
            cb: (data) => {
                this.renderContent(data);
            }
        });
        this.fixAni(this.options.placement);
        this.lastPlace = '';
        this.handleFooter();
        this.handleTools();
        this.bindTrigger();
        this.setDraggable();
        this.contEl.querySelectorAll(`[${alias}="closebubble"]`).forEach((k) => {
            k.removeEventListener('click', this.triggerClose);
            k.addEventListener('click', this.triggerClose, false);
        });
        if (['confirm', 'alert'].includes(this.options.feature)) {
            this.removePulseEvt();
            this.addPulseEvt();
        }
        this.initBullets();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    setFeature() {
        if (!this.options.feature)
            return;
        if (['alert', 'prompt', 'notice'].includes(this.options.feature)) {
            this.options.footer.layout = 'right';
        }
        if (['confirm', 'alert', 'prompt'].includes(this.options.feature)) {
            this.options.mask.closable = false;
            this.options.placement = 'center-top';
            this.options.padding.value = `${prefix}p-md`;
            this.options.in = 'fadeInDown';
            this.options.out = 'fadeOutDown';
            this.options.tools.enable = false;
            if (this.options.feature === 'alert') {
                this.options.footer.children = ['confirm'];
            }
            else if (this.options.feature === 'prompt') {
                this.options.footer.children = ['close', 'clear', 'confirm'];
            }
        }
        else if (this.options.feature === 'notice') {
            this.options.mask.enable = false;
            this.options.placement = 'left-bottom';
        }
    }
    addPulseEvt() {
        this.maskEl.addEventListener('click', this.addPulseAnim, false);
        this.mainEl.addEventListener('animationend', this.removePulseAnim, false);
    }
    removePulseEvt() {
        this.maskEl.removeEventListener('click', this.addPulseAnim);
        this.mainEl.removeEventListener('animationend', this.removePulseAnim);
    }
    fixAni(placement) {
        this.aniIn = placement === 'max' && this.options.in === 'slideDown' ? 'fadeIn' : this.options.in;
        this.aniOut = placement === 'max' && this.options.out === 'slideUp' ? 'fadeOut' : this.options.out;
    }
    handleTools() {
        if (this.options.tools.enable) {
            this.options.tools.children.forEach((k) => {
                k.wrapEl.querySelector(`[${alias}="icon"]`);
                if (k.name === 'close') {
                    k.wrapEl.onclick = () => {
                        this.hide();
                    };
                }
                else if (k.name === 'widen') {
                    k.wrapEl.onclick = () => {
                        if (this.lastSize === 'max') {
                            classes(k.iconEl).replace(k.icon, k.swap);
                            this.mainEl.setAttribute('size', this.options.size);
                            this.lastSize = this.options.size;
                        }
                        else {
                            classes(k.iconEl).replace(k.swap, k.icon);
                            this.mainEl.setAttribute('size', 'max');
                            this.lastSize = 'max';
                        }
                    };
                }
                else if (k.name === 'enlarge') {
                    k.wrapEl.onclick = () => {
                        if (this.lastPlacement === 'center-max') {
                            classes(k.iconEl).replace(k.icon, k.swap);
                            this.mainEl.setAttribute('placement', this.options.placement);
                            this.lastPlacement = this.options.placement;
                        }
                        else {
                            classes(k.iconEl).replace(k.swap, k.icon);
                            this.mainEl.setAttribute('placement', 'center-max');
                            this.lastPlacement = 'center-max';
                        }
                    };
                }
                else {
                    k.action && k.action.call(this, k);
                }
            });
        }
    }
    setAttrs() {
        this.options.classes && classes(this.mainEl).add(this.options.classes);
        this.options.size ? this.mainEl.setAttribute('size', this.options.size) : this.mainEl.removeAttribute('size');
        this.options.contType ? this.mainEl.setAttribute('conttype', this.options.contType) : this.mainEl.removeAttribute('conttype');
        this.contEl.toggleAttribute('dedicated', this.options.dedicated);
        this.options.placement ? this.mainEl.setAttribute('placement', this.options.placement) : this.mainEl.removeAttribute('placement', this.options.placement);
        this.options.zIndex ? addStyle(this.mainEl, 'z-index', this.options.zIndex) : removeStyle(this.mainEl, 'z-index');
        this.options.tools.enable ? this.mainEl.setAttribute('toolsplace', this.options.tools.placement) : this.mainEl.removeAttribute('toolsplace');
        this.options.feature ? this.mainEl.setAttribute('feature', this.options.feature) : this.mainEl.removeAttribute('feature');
        if (this.parentEl !== document.body) {
            let pStyle = style(this.parentEl);
            pStyle.position === 'static' && addStyle(this.parentEl, 'position', 'relative');
            pStyle.overflow !== 'hidden' && addStyle(this.parentEl, 'overflow', 'hidden');
            this.mainEl.toggleAttribute('restrict', true);
        }
        else {
            this.mainEl.removeAttribute('restrict');
        }
    }
    setDraggable() {
        if (!this.headEl || !this.options.draggable)
            return;
        this.gestureIns = new Gesture(this.wrapEl, extend({
            target: {
                viewport: {
                    enable: true,
                    selector: this.parentEl,
                },
                translate: {
                    target: this.headEl,
                },
                onTranslating: (data) => {
                    transformTools.set({
                        el: this.wrapEl,
                        data: { translate: data.translate.value }
                    });
                },
            },
            source: this.options.gesture,
        }));
    }
    bindTrigger() {
        if (this.options.trigger === 'click') {
            if (this.targetEl && !this.canTrigger) {
                this.targetEl.removeEventListener('click', this.triggerShow);
                this.targetEl.addEventListener('click', this.triggerShow, false);
            }
            if (this.wings.length > 0 && !this.canTrigger) {
                this.wings.forEach((k) => {
                    k.removeEventListener('click', this.triggerShow);
                    k.addEventListener('click', this.triggerShow, false);
                });
            }
        }
        else if (this.options.trigger === 'sticky') {
            this.show();
        }
        if (this.maskEl && this.options.mask.closable) {
            this.maskEl.removeEventListener('click', this.triggerShow);
            this.maskEl.addEventListener('click', this.triggerShow, false);
        }
        this.canTrigger = true;
    }
    unbindTrigger(mode = 'host') {
        this.canTrigger = false;
        if (this.options.trigger === 'click') {
            this.targetEl.removeEventListener('click', this.triggerShow);
            if (this.wings.length > 0 && mode === 'all') {
                this.wings.forEach((item) => {
                    item.removeEventListener('click', this.triggerShow);
                });
            }
        }
        if (this.maskEl && this.options.mask.closable && mode === 'all') {
            this.maskEl.removeEventListener('click', this.triggerShow);
        }
    }
    
    async show(cb) {
        if (this.destroyed || this.state !== 'hidden' || this.options.asleep) {
            return this;
        }
        this.state = 'ing';
        this.options.b4Show && await this.options.b4Show.call(this);
        elState(this.mainEl).isVirtual && this.parentEl.appendChild(this.mainEl);
        super.listen({ name: 'show', cb });
        this.wrapHeight = parseInt(style(this.wrapEl).height);
        this.duration = this.options.duration || (this.options.autoDur ? getAutoDur(this.wrapHeight) : parseFloat(style(this.wrapEl).animationDuration) * 1000);
        this.wrapEl.style.transitionDuration = `${this.duration}ms`;
        this.targetEl && this.targetEl.classList.add(this.options.actClass);
        if (this.aniIn === 'slideDown') {
            easeHeight({ el: this.wrapEl, height: this.wrapHeight, type: 'down', duration: this.duration });
        }
        else {
            this.options.duration && (this.wrapEl.style.animationDuration = `${this.options.duration}ms`);
            this.aniIn && (this.wrapEl.style.animationName = prefix + this.aniIn);
        }
        this.lastShowTime = Date.now();
        this.mainEl.setAttribute('show', '');
        await delay({
            duration: this.duration,
            done: () => {
                this.state = 'shown';
                super.listen({ name: 'shown', cb });
            }
        });
        return this;
    }
    
    async hide(cb) {
        if (this.destroyed || this.state !== 'shown') {
            return this;
        }
        this.state = 'ing';
        if (!this.options.deadShow) {
            this.options.b4Hide && await this.options.b4Hide.call(this);
            super.listen({ name: 'hide', cb });
            this.targetEl ? this.targetEl.classList.remove(this.options.actClass) : null;
            this.options.wing.actClass && this.wings.map((k) => { k.classList.remove(this.options.wing.actClass); });
            this.maskEl && (this.maskEl.style.opacity = 0);
            if (this.aniOut === 'slideUp') {
                easeHeight({ el: this.wrapEl, height: this.wrapHeight, type: 'up', duration: this.duration });
            }
            else {
                this.aniOut && (this.wrapEl.style.animationName = prefix + this.aniOut);
            }
            this.mainEl.querySelectorAll('video,audio').forEach((k) => {
                k.pause();
            });
            await delay({
                duration: this.duration,
                done: () => {
                    this.state = 'hidden';
                    this.mainEl.removeAttribute('show');
                    this.maskEl && this.maskEl.removeAttribute("style");
                    this.wrapEl.removeAttribute("style");
                    this.mainEl.remove();
                    super.listen({ name: 'hidden', cb });
                    this.aniOut === 'slideUp' && (this.wrapEl.style.height = 'auto');
                }
            });
        }
        return this;
    }
}

const confirm = ({ content, contType, contData, tplStr, tplEng, heading, yes, no }) => {
    if (isEmpty(content))
        return;
    return new Promise((resolve) => {
        new Dialog(null, {
            content,
            contType,
            contData,
            heading,
            tplStr,
            tplEng,
            feature: 'confirm',
            onConfirmed: () => {
                yes && yes();
                resolve(true);
            },
            onCanceled: () => {
                no && no();
                resolve(false);
            }
        }).show();
    });
};

const alert = ({ content, contType, contData, heading, tplStr, tplEng, yes }) => {
    if (isEmpty(content))
        return;
    return new Promise((resolve) => {
        new Dialog(null, {
            content,
            contType,
            contData,
            heading,
            tplStr,
            tplEng,
            feature: 'alert',
            onConfirmed: () => {
                yes && yes();
                resolve(true);
            },
        }).show();
    });
};

const notice = ({ content, contType = '', contData = {}, heading = '', label = '', tplStr, tplEng, yes }) => {
    if (isEmpty(content))
        return;
    return new Promise((resolve) => {
        new Dialog(null, {
            content,
            contType,
            contData,
            heading,
            tplStr,
            tplEng,
            feature: 'notice',
            footer: {
                children: [{
                        name: 'confirm',
                        label,
                    }]
            },
            onConfirmed: () => {
                yes && yes();
                resolve(true);
            },
        }).show();
    });
};

const prompt = (options) => {
    let opts = Object.assign({ fields: [{ type: 'input' }] }, options), data = Array.isArray(opts.fields) ? opts.fields : [opts.fields], frags = document.createDocumentFragment();
    for (let k of data) {
        let label = k.label ? `<div class="${prefix}field-label">${k.label}</div>` : '', note = k.note ? `<div  class="${prefix}field-note">${k.note}</div>` : '', attrs = Object.assign(k.type === 'upload' ? { feature: 'gallery' } : k.type === 'datetime' ? { display: 'inline', 'auto-fill': true, } : {}, k.attrs), input = createEl(`ax-${k.type || 'input'}`, { ...attrs }), tpl = `
            <section class="${prefix}field ${prefix}field-apart">
                ${label}
                <div class="${prefix}field-cont">
                    <div class="${prefix}field-input"></div>
                    ${note}
                </div>
            </section>
        `, dom = tplToEl(tpl);
        dom.querySelector(`.${prefix}field-input`).appendChild(input);
        frags.appendChild(dom);
        k.field = input;
    }
    return new Promise((resolve) => {
        new Dialog(null, {
            heading: opts.heading,
            feature: 'prompt',
            onInitiated: function () {
                this.contEl.appendChild(frags);
            },
            onConfirmed: () => {
                let maps = data.map((k) => { return { type: k.type, label: k.label, value: k.field.value }; }), vals = maps.map((k) => k.value), params = { value: vals, fields: maps };
                opts.yes && opts.yes(params);
                resolve(params);
            },
            onCleared: () => {
                for (let k of data)
                    k.field.clear();
            },
            onClosed: () => {
            }
        }).show();
    });
};

const removeStyles = (el, props) => {
    let target = getEl(el);
    if (!target || isEmpty(props))
        return;
    for (let k of props)
        removeStyle(target, k);
};

const addStyles = (el, data) => {
    let target = getEl(el);
    if (!target || isEmpty(data))
        return;
    for (let k of Array.isArray(data) ? data : [data]) {
        addStyle(target, k.key, k.value);
    }
};

const regElem = (comp) => {
    if (!comp || getDataType(comp) !== 'Class')
        return;
    window.customElements.define(`ax-${comp.name.replace('Elem', '').toLowerCase()}`, comp);
};

const getImgSpin = () => getComputedVar(`--${prefix}spin`).split('"')[1];

const getImgSpinDk = () => getComputedVar(`--${prefix}spin-dk`).split('"')[1];

const getImgNone = () => getComputedVar(`--${prefix}none`).split('"')[1];

const getImgEmpty = () => getComputedVar(`--${prefix}empty`).split('"')[1];

const getImgAvatar = () => getComputedVar(`--${prefix}avatar`).split('"')[1];

class ModBaseListenCacheNest extends ModBaseListenCache {
    flatData;
    treeData;
    treeInputEl;
    check(param, bool) {
    }
    updateArrowEl(param) {
    }
    eachCollapse(param) {
    }
    eachExpand(param) {
    }
    initCheckeds() {
        if (!this.options.check.enable)
            return;
        let vals = valToArr(this.options.check.value).map((k) => findItem(k, this.flatData)).filter(Boolean), items = this.flatData.filter((k) => k.checked);
        for (let k of items)
            k.checked = false;
        this.check([...items, ...vals]);
    }
    initReadonlys() {
        this.readonly(valToArr(this.options.readonly));
    }
    initDisableds() {
        this.disable(valToArr(this.options.disable));
    }
    getReadonlys() {
        return (this.flatData || this.treeData).filter((k) => k.readonly);
    }
    getDisableds() {
        return (this.flatData || this.treeData).filter((k) => k.disabled);
    }
    getExpandeds() {
        return (this.flatData || this.treeData).filter((k) => k.expanded);
    }
    getCheckeds() {
        return (this.flatData || this.treeData).filter((k) => k.checked);
    }
    getUncheckeds() {
        return (this.flatData || this.treeData).filter((k) => !k.checked);
    }
    getArrowEl(item) {
        if (!this.options.arrow.enable)
            return;
        item.arrowEl = createEl('i', { [alias]: 'arrow' });
        this.updateArrowEl(item);
    }
    toggleArrow(val, item) {
        let map = val ? { new: 'show', old: 'hide', action: 'add' } : { new: 'hide', old: 'show', action: 'remove' };
        item.headEl.toggleAttribute('expanded', val);
        if (this.options.arrow.type === 'image') {
            item.arrowEl.style.backgroundImage = `url("${this.options.arrow[map.new]}")`;
        }
        else {
            if (this.options.arrow.show === this.options.arrow.hide) {
                item.arrowEl.classList[map.action](this.options.arrow.anim);
            }
            else {
                classes(item.arrowEl).replace(this.options.arrow[map.old], this.options.arrow[map.new]);
            }
        }
    }
    async collapse(data, cb) {
        if (this.destroyed)
            return;
        let tmp = Array.isArray(data) ? data : [data];
        for (let k of tmp) {
            await this.eachCollapse(k);
        }
        cb && cb.call(this, this.getExpandeds());
        return this;
    }
    async expand(data, cb) {
        if (this.destroyed)
            return;
        let tmp = Array.isArray(data) ? data : [data];
        for (let k of tmp) {
            await this.eachExpand(k);
        }
        cb && cb.call(this, this.getExpandeds());
        return this;
    }
    collapseAll(cb) {
        if (this.destroyed)
            return;
        for (let k of this.getExpandeds())
            this.eachCollapse(k);
        this.listen({ name: 'collapsedAll', cb });
        return this;
    }
    expandAll(cb) {
        if (this.destroyed)
            return;
        for (let k of this.flatData) {
            k.children && !k.expanded && this.eachExpand(k);
        }
        this.listen({ name: 'expandedAll', cb });
        return this;
    }
    lock(cb) {
        if (this.destroyed || !this.targetEl)
            return;
        this.targetEl.toggleAttribute('inert', true);
        this.destroyed = true;
        this.treeInputEl.disabled = true;
        this.listen({ name: 'locked', cb });
        return this;
    }
    unlock(cb) {
        if (!this.targetEl)
            return;
        this.targetEl.toggleAttribute('inert', false);
        this.destroyed = false;
        this.treeInputEl.disabled = false;
        this.listen({ name: 'unlocked', cb });
        return this;
    }
    readonly(data, cb) {
        if (this.destroyed)
            return;
        let tmp = isNull(data) ? this.flatData : (Array.isArray(data) ? data : [data]), items = tmp.map((k) => findItem(k, this.flatData)).filter(Boolean);
        for (let k of items)
            k.readonly = true;
        this.listen({ name: 'readonly', cb, params: [items] });
        return this;
    }
    readonlyAll(cb) {
        if (this.destroyed)
            return;
        for (let k of this.flatData)
            k.readonly = true;
        this.listen({ name: 'readonlyAll', cb });
        return this;
    }
    disable(data, cb) {
        if (this.destroyed)
            return;
        let tmp = isNull(data) ? this.flatData : (Array.isArray(data) ? data : [data]), items = tmp.map((k) => findItem(k, this.flatData)).filter(Boolean);
        for (let k of items)
            k.disabled = true;
        this.listen({ name: 'disabled', cb, params: [items] });
        return this;
    }
    disableAll(cb) {
        if (this.destroyed)
            return;
        for (let k of this.flatData)
            k.disabled = true;
        this.listen({ name: 'disabledAll', cb });
        return this;
    }
    enable(data, cb) {
        if (this.destroyed)
            return;
        let tmp = isNull(data) ? this.flatData : (Array.isArray(data) ? data : [data]), items = tmp.map((k) => findItem(k, this.flatData)).filter((k) => k && (k.disabled || k.readonly));
        for (let k of items) {
            k.hasOwnProperty('disabled') && (k.disabled = false);
            k.hasOwnProperty('readonly') && (k.readonly = false);
        }
        this.listen({ name: 'enabled', cb, params: [items] });
        return this;
    }
    enableAll(cb) {
        if (this.destroyed)
            return;
        this.enable(this.flatData);
        this.listen({ name: 'enabledAll', cb });
        return this;
    }
    toggleCheck(item) {
        item.checked ? this.check(item, false) : this.check(item, true);
    }
    uncheckAll(cb) {
        if (this.destroyed || !this.options.check.enable)
            return;
        for (let k of this.flatData) {
            k.checked = false;
        }
        this.listen({ name: 'uncheckedAll', cb });
        return this;
    }
    getSiblings(item) {
        return this.flatData.filter((k) => k.pId == item.pId && k !== item);
    }
    updateElCont(item, value, type = 'brief') {
        let prop = type === 'content' ? 'cont' : type, val = isNull(value) ? item[type] : value, valType = getDataType(val);
        valType.includes('HTML') ? (item[prop + 'El'].innerHTML = '', item[prop + 'El'].appendChild(val)) : (item[prop + 'El'].innerHTML = val);
    }
    async getElCont({ target, data = {}, prop = 'brief', cb }) {
        let elProp = prop === 'content' ? 'contEl' : `${prop}El`;
        await getContent.call(this, {
            content: data?.content || target[prop],
            contType: data?.contType || 'text',
            contData: data?.contData || { ...target },
            ajax: {
                xhrName: 'contXhr',
                target: target[elProp],
                ...(data?.ajax || {})
            },
            request: (data) => {
                this.listen({ name: 'request', params: [data] });
            },
            cb: async (resp) => {
                let content = ['Array', 'Object'].includes(getDataType(resp)) ? { ...target, [prop]: resp } : resp;
                setContent({
                    content,
                    template: data?.tplStr || this.tplStr,
                    engine: data?.tplEng || this.tplEng,
                    prevent: (cont) => {
                        cb && cb(cont, target);
                    }
                });
            }
        });
    }
    async setElCont({ item, data = {}, prop = 'brief', cb }) {
        let target = findItem(item, this.flatData);
        if (!target)
            return;
        elState(target.bodyEl).isVirtual && target.headEl.insertAdjacentElement('afterend', target.bodyEl);
        await this.getElCont({
            target,
            data,
            cb: (cont) => {
                if (target[prop] === cont) {
                    this.updateElCont(target, cont, prop);
                }
                else {
                    target[prop] = cont;
                    !isProxy(target) && this.updateElCont(target, cont, prop);
                }
                cb && cb(cont, target);
            }
        });
    }
}

let AXTMP_textFold = config.lang.more.fold, AXTMP_textUnfold = config.lang.more.unfold;
const optMore = [
    {
        attr: 'stor-keys',
        prop: 'storKeys',
        value: ['content', 'length', 'folded'],
    },
    {
        attr: 'content',
        prop: 'content',
        value: ''
    },
    {
        attr: 'length',
        prop: 'length',
        value: 60,
    },
    {
        attr: 'folded',
        prop: 'folded',
        value: true,
    },
    {
        attr: 'text-fold',
        prop: 'textFold',
        value: AXTMP_textFold,
    },
    {
        attr: 'text-unfold',
        prop: 'textUnfold',
        value: AXTMP_textUnfold,
    },
    {
        attr: 'class-fold',
        prop: 'classFold',
        value: `${prefix}c-prim`,
    },
    {
        attr: 'class-unfold',
        prop: 'classUnfold',
        value: `${prefix}c-prim`,
    },
    {
        attr: 'on-fold',
        prop: 'onFold',
        value: null,
    },
    {
        attr: 'on-unfold',
        prop: 'onUnfold',
        value: null,
    },
    {
        attr: 'on-updatedcont',
        prop: 'onUpdatedCont',
        value: null,
    },
    ...optBase
];

class More extends ModBaseListenCache {
    options = {};
    intercepted;
    button;
    content;
    folded;
    handlers;
    html;
    pureHtml;
    event;
    text;
    ell;
    static hostType = 'node';
    static optMaps = optMore;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            maps: More.optMaps,
            host: elem,
            component: true,
        });
        
        this.content = '';
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        
        this.folded = this.options.folded;
        this.getPureContent();
        this.targetEl.innerHTML = this.content;
        this.setAttrs();
        if (this.content.length > this.options.length) {
            this.intercepted = this.content.substring(0, this.options.length) + '...';
            this.button = createEl('i');
            this.targetEl.appendChild(this.button);
            this.toggle(this.folded ? 'fold' : 'unfold');
            this.button.onclick = () => {
                this.folded ? this.toggle('unfold') : this.toggle('fold');
            };
        }
        super.listen({ name: 'initiated', cb });
        return this;
    }
    
    
    setAttrs() {
        
    }
    
    toggle(type) {
        if (type === 'unfold') {
            this.targetEl.removeChild(this.button.previousSibling);
            this.button.innerHTML = this.options.textUnfold;
            this.button.setAttribute('class', this.options.classUnfold);
            this.button.insertAdjacentHTML('beforeBegin', this.content);
            super.listen({ name: 'unfold' });
            this.folded = false;
        }
        else if (type === 'fold') {
            this.targetEl.removeChild(this.button.previousSibling);
            this.button.innerHTML = this.options.textFold;
            this.button.setAttribute('class', this.options.classFold);
            this.button.insertAdjacentHTML('beforeBegin', this.intercepted);
            super.listen({ name: 'fold' });
            this.folded = true;
        }
        super.updateCache({ folded: this.folded });
    }
    
    getPureContent() {
        if (this.options.content && typeof this.options.content === 'string') {
            this.content = super.getStrFromContent(this.options.content);
        }
        else {
            this.content = this.rawHtml ? this.rawHtml : this.targetEl.innerHTML;
        }
        this.content = purifyHtml(this.content);
    }
    
    destroy(cb) {
        if (this.destroyed) {
            return this;
        }
        this.button.onclick = null;
        this.clearCache();
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
    
    
    async updateCont(text, cb) {
        if (this.destroyed) {
            return this;
        }
        this.options.content = text;
        await this.init();
        super.updateCache({ content: this.content });
        super.listen({ name: 'updatedCont', cb, params: [this.content] });
        return this;
    }
}

const optPosition = [
    {
        attr: 'enable',
        prop: 'enable',
        value: true,
    },
    {
        attr: 'placement',
        prop: 'placement',
        value: 'top-center',
    },
    {
        attr: 'arrow',
        prop: 'arrow',
        value: {
            enable: true,
            size: '0.5rem',
            offset: '2.8rem',
            gap: '.8rem',
        }
    },
    {
        attr: 'parent-mutation',
        prop: 'parentMutation',
        value: {
            enable: true,
            selector: document.body,
            options: {
                attributes: true,
                characterData: true,
                childList: true,
                subtree: true,
            }
        }
    },
    {
        attr: 'target-obs',
        prop: 'targetObs',
        value: {
            enable: true,
            attrs: ['class'],
            size: true,
        }
    },
    {
        attr: 'bubble-obs',
        prop: 'bubbleObs',
        value: {
            enable: true,
            attrs: ['class'],
            size: true,
        }
    },
    {
        attr: 'delay',
        prop: 'delay',
        value: 20,
    },
    {
        attr: 'on-transferred',
        prop: 'onTransferred',
        value: null,
    },
    {
        attr: 'on-reposition',
        prop: 'onReposition',
        value: null,
    },
    ...optBase
];

class Position extends ModBaseListen {
    options = {};
    bubbleEl;
    arrowEl;
    gap;
    parent;
    placement;
    observerCount;
    completeCount;
    trigger;
    targetAttrsObs;
    targetSizeObs;
    bubbleAttrsObs;
    bubbleSizeObs;
    parentObs;
    bubbleData;
    browserData;
    bodyData;
    regularPlaces;
    specialPlaces;
    places;
    timer;
    fullGap;
    static hostType = 'none';
    static optMaps = optPosition;
    constructor(target, bubble, options = {}, initial = config.initial) {
        super();
        super.ready({
            type: Position.hostType,
            options,
            host: target,
            maps: Position.optMaps,
            spread: ['arrow', 'parentMutation', 'targetObs', 'bubbleObs']
        });
        
        this.targetEl = getEl(target);
        this.bubbleEl = getEl(bubble);
        if (!this.targetEl || !this.bubbleEl) {
            throw new Error(`The host element and guest element must be exist!`);
        }
        
        this.gap = toPixel(this.options.arrow.gap);
        
        this.parent = this.options.parentMutation.enable && getEl(this.options.parentMutation.selector) ? getEl(this.options.parentMutation.selector) : null;
        this.regularPlaces = [
            'left-start', 'left-center', 'left-end',
            'right-start', 'right-center', 'right-end',
            'top-start', 'top-center', 'top-end',
            'bottom-start', 'bottom-center', 'bottom-end',
        ];
        this.specialPlaces = ['left-max', 'right-max', 'top-max', 'bottom-max', 'center', 'center-max',];
        this.places = [...this.regularPlaces, ...this.specialPlaces];
        this.observerCount = 0;
        this.completeCount = 0;
        this.timer = 0;
        this.trigger = () => {
            if (elState(this.bubbleEl).isUncalc)
                return;
            this.observerCount++;
            this.complete(this.observerCount);
        };
        this.targetAttrsObs = new MutationObserver((mutations) => {
            for (let mutation of mutations) {
                if (this.options.targetObs.attrs.includes(mutation.attributeName)) {
                    this.trigger();
                }
            }
        });
        this.targetSizeObs = new ResizeObserver(() => {
            this.trigger();
        });
        this.bubbleAttrsObs = new MutationObserver((mutations) => {
            for (let mutation of mutations) {
                if (this.options.bubbleObs.attrs.includes(mutation.attributeName)) {
                    this.trigger();
                }
            }
        });
        this.bubbleSizeObs = new ResizeObserver(() => {
            this.trigger();
        });
        this.parentObs = new MutationObserver(() => {
            if (!this.unsettable('target') && this.targetData) {
                if (offset(this.targetEl).left !== this.targetData.left || offset(this.targetEl).top !== this.targetData.top) {
                    this.trigger();
                }
            }
        });
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        
        this.placement = this.correctPlace(this.options.placement);
        if (!this.places.includes(this.placement))
            return this;
        this.options.placement = this.placement;
        this.fullGap = getFullGap();
        !['center', 'center-max'].includes(this.placement) && this.createArrow();
        this.setAttrs();
        this.resetPlacement();
        this.options.enable && this.setObs();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    setObs() {
        window.addEventListener("resize", this.trigger);
        window.addEventListener('scroll', this.trigger);
        if (this.options.targetObs.enable) {
            if (this.options.targetObs.attrs.length > 0) {
                this.targetAttrsObs.observe(this.targetEl, {
                    attributes: true,
                });
            }
            this.options.targetObs.size ? this.targetSizeObs.observe(this.targetEl) : null;
        }
        if (this.options.bubbleObs.enable) {
            if (this.options.bubbleObs.attrs.length > 0) {
                this.bubbleAttrsObs.observe(this.bubbleEl, {
                    attributes: true,
                });
            }
            this.options.bubbleObs.size ? this.bubbleSizeObs.observe(this.bubbleEl) : null;
        }
        if (this.parent) {
            this.parentObs.observe(this.parent, this.options.parentMutation.options);
        }
    }
    removeObs() {
        if (this.options.targetObs.enable) {
            this.options.targetObs.attrs.length > 0 ? this.targetAttrsObs.disconnect() : null;
            this.options.targetObs.size ? this.targetSizeObs.disconnect() : null;
        }
        if (this.options.bubbleObs.enable) {
            this.options.bubbleObs.attrs.length > 0 ? this.bubbleAttrsObs.disconnect() : null;
            this.options.bubbleObs.size ? this.bubbleSizeObs.disconnect() : null;
        }
        this.parent ? this.parentObs.disconnect() : null;
        window.removeEventListener('resize', this.trigger);
        window.removeEventListener('scorll', this.trigger);
    }
    correctPlace(value) {
        return (value === 'left') ? 'left-center' :
            (value === 'right') ? 'right-center' :
                (value === 'top') ? 'top-center' :
                    (value === 'bottom') ? 'bottom-center' :
                        (value === 'max') ? 'center-max' : value;
    }
    complete(count) {
        
        clearInterval(this.timer);
        this.timer = setInterval(() => {
            if (this.observerCount === count) {
                this.resetPlacement();
                this.observerCount = 0;
                clearInterval(this.timer);
            }
        }, this.options.delay);
    }
    
    setAttrs() {
        this.bubbleEl.style.position = (['center', 'center-max'].includes(this.placement)) ? 'fixed' : 'absolute';
        this.setPlacementAttr();
    }
    setPlacementAttr() {
        this.bubbleEl.setAttribute('placement', this.placement);
    }
    createArrow() {
        if (this.options.arrow.enable) {
            this.arrowEl = createEl('i', { style: 'position:absolute', [alias]: 'arrow' });
            this.bubbleEl.appendChild(this.arrowEl);
        }
    }
    setArrow(placement) {
        let [left, right, top, bottom] = ['auto', 'auto', 'auto', 'auto'], exceedWidth = this.targetData.width > this.bubbleData.width / 2, exceedHeight = this.targetData.height > this.bubbleData.height / 2, arrowHalf = toPixel(getComputedStyle(this.arrowEl).width) / 2, arrowOffset = toPixel(this.options.arrow.offset), arrowRotate = '';
        if (placement.includes('top') || placement.includes('bottom')) {
            if (placement.includes('max')) {
                let targetLeft = this.targetData.offsetLeft + this.targetData.width / 2, exceedStart = targetLeft < this.bubbleData.offsetLeft + arrowOffset - arrowHalf, exceedEnd = targetLeft > this.bubbleData.offsetRight - arrowOffset + arrowHalf;
                left = (exceedEnd || exceedStart) ? '-10000px' : (this.targetData.offsetLeft - this.bubbleData.offsetLeft) + this.targetData.width / 2 - arrowHalf + 'px';
            }
            else if (placement.includes('start')) {
                left = !exceedWidth ? Math.floor(this.targetData.width / 2 - arrowHalf) + 'px' : this.options.arrow.offset;
            }
            else if (placement.includes('end')) {
                right = !exceedWidth ? Math.floor(this.targetData.width / 2 - arrowHalf) + 'px' : this.options.arrow.offset;
            }
            else {
                left = this.bubbleData.width / 2 - 1 - arrowHalf + 'px';
            }
        }
        else if (placement.includes('left') || placement.includes('right')) {
            if (placement.includes('max')) {
                let targetTop = this.targetData.offsetTop + this.targetData.height / 2, exceedStart = targetTop < this.bubbleData.offsetTop + arrowOffset - arrowHalf, exceedEnd = targetTop > this.bubbleData.offsetBottom - arrowOffset + arrowHalf;
                top = (exceedEnd || exceedStart) ? '-10000px' : (this.targetData.offsetTop - this.bubbleData.offsetTop) + this.targetData.height / 2 - arrowHalf + 'px';
            }
            else if (placement.includes('start')) {
                top = !exceedHeight ? Math.floor(this.targetData.height / 2 - arrowHalf) + 'px' : this.options.arrow.offset;
            }
            else if (placement.includes('end')) {
                bottom = !exceedHeight ? Math.floor(this.targetData.height / 2 - arrowHalf) + 'px' : this.options.arrow.offset;
            }
            else {
                top = this.bubbleData.height / 2 - 1 - arrowHalf + 'px';
            }
        }
        else {
            left = '-10000px';
            top = '-10000px';
        }
        if (placement.includes('top')) {
            bottom = '-' + this.options.arrow.size;
            arrowRotate = '45';
        }
        else if (placement.includes('bottom')) {
            top = '-' + this.options.arrow.size;
            arrowRotate = '-135';
        }
        else if (placement.includes('left')) {
            right = '-' + this.options.arrow.size;
            arrowRotate = '-45';
        }
        else if (placement.includes('right')) {
            left = '-' + this.options.arrow.size;
            arrowRotate = '135';
        }
        this.arrowEl.style.left = left;
        this.arrowEl.style.right = right;
        this.arrowEl.style.top = top;
        this.arrowEl.style.bottom = bottom;
        this.arrowEl.style.transform = `rotate(${arrowRotate}deg)`;
    }
    getRectsData() {
        this.bodyData = {
            width: document.body.offsetWidth,
            height: document.body.offsetHeight,
        };
        this.browserData = {
            width: document.documentElement.clientWidth,
            height: document.documentElement.clientHeight,
            scrollTop: document.documentElement.scrollTop || document.body.scrollTop,
            scrollLeft: document.documentElement.scrollLeft || document.body.scrollLeft
        };
        let targetRect = this.targetEl.getBoundingClientRect();
        this.targetData = {
            width: targetRect.width,
            height: targetRect.height,
            offsetLeft: targetRect.left,
            offsetRight: targetRect.right,
            offsetTop: targetRect.top,
            offsetBottom: targetRect.bottom,
            gapRight: this.browserData.width - targetRect.right,
            gapBottom: this.browserData.height - targetRect.bottom,
            ...offset(this.targetEl)
        };
        this.getBubbleData();
    }
    getBubbleData() {
        let bubbleRect = this.bubbleEl.getBoundingClientRect();
        this.bubbleData = {
            width: bubbleRect.width,
            height: bubbleRect.height,
            offsetLeft: bubbleRect.left,
            offsetRight: bubbleRect.right,
            offsetTop: bubbleRect.top,
            offsetBottom: bubbleRect.bottom,
            gapRight: this.browserData.width - bubbleRect.right,
            gapBottom: this.browserData.height - bubbleRect.bottom
        };
    }
    
    setPlacement(placement) {
        let [left, right, top, bottom] = ['auto', 'auto', 'auto', 'auto'];
        if (placement.includes('bottom') || placement.includes('top')) {
            if (placement.includes('bottom')) {
                left = placement === 'bottom' || placement === 'bottom-center' ? (this.targetData.left - (this.bubbleData.width - this.targetData.width) / 2) :
                    placement === 'bottom-start' ? this.targetData.left :
                        placement === 'bottom-end' ? this.targetData.left + this.targetData.width - this.bubbleData.width : 0;
                top = `${toNumber(this.targetData.top + this.targetData.height + this.gap, { mode: 'ceil' })}px`;
                left = `${toNumber(left, { mode: 'ceil' })}px`;
            }
            else if (placement.includes('top')) {
                left = placement === 'top' || placement === 'top-center' ? (this.targetData.left - (this.bubbleData.width - this.targetData.width) / 2) :
                    placement === 'top-start' ? this.targetData.left :
                        placement === 'top-end' ? (this.targetData.left + this.targetData.width - this.bubbleData.width) : 0;
                bottom = `${toNumber((this.bodyData.height - this.targetData.top + this.gap), { mode: 'ceil' })}px`;
                left = `${toNumber(left, { mode: 'ceil' })}px`;
            }
        }
        else if (placement.includes('left') || placement.includes('right')) {
            if (placement.includes('left')) {
                if (placement === 'left' || placement === 'left-center') {
                    top = `${toNumber(this.targetData.top - (this.bubbleData.height - this.targetData.height) / 2, { mode: 'ceil' })}px`;
                }
                else if (placement === 'left-start') {
                    top = `${toNumber(this.targetData.top, { mode: 'ceil' })}px`;
                }
                else if (placement === 'left-end') {
                    bottom = `${toNumber(this.bodyData.height - this.targetData.top - this.targetData.height, { mode: 'ceil' })}px`;
                }
                right = `${toNumber(this.browserData.width - this.targetData.left + this.gap, { mode: 'ceil' })}px`;
            }
            else if (placement.includes('right')) {
                if (placement === 'right' || placement === 'right-center') {
                    top = `${toNumber(this.targetData.top - (this.bubbleData.height - this.targetData.height) / 2, { mode: 'ceil' })}px`;
                }
                else if (placement === 'right-start') {
                    top = `${toNumber(this.targetData.top, { mode: 'ceil' })}px`;
                }
                else if (placement === 'right-end') {
                    bottom = `${toNumber(this.bodyData.height - this.targetData.top - this.targetData.height, { mode: 'ceil' })}px`;
                }
                left = `${toNumber(this.targetData.left + this.targetData.width + this.gap, { mode: 'ceil' })}px`;
            }
        }
        if (this.placement === 'center-max') {
            this.bubbleEl.style.width = `calc(100vw - ${this.fullGap}*2)`;
            this.bubbleEl.style.height = `calc(100vh - ${this.fullGap}*2)`;
            left = this.fullGap;
            top = this.fullGap;
        }
        else if (this.placement === 'center') {
            this.bubbleEl.style.marginLeft = `-${toNumber(this.bubbleEl.getBoundingClientRect().width / 2, { mode: 'ceil' })}px`;
            this.bubbleEl.style.marginTop = `-${toNumber(this.bubbleEl.getBoundingClientRect().height / 2, { mode: 'ceil' })}px`;
            left = '50%';
            top = '50%';
        }
        else if (this.placement === 'top-max' || this.placement === 'bottom-max') {
            left = `${toNumber(this.browserData.scrollLeft + toPixel(this.fullGap), { mode: 'ceil' })}px`;
            this.bubbleEl.style.width = `calc(100vw - ${this.fullGap}*2)`;
        }
        else if (this.placement === 'left-max' || this.placement === 'right-max') {
            top = `${toNumber(this.browserData.scrollTop + toPixel(this.fullGap), { mode: 'ceil' })}px`;
            this.bubbleEl.style.height = `calc(100vh - ${this.fullGap}*2)`;
        }
        this.bubbleEl.style.left = left;
        this.bubbleEl.style.right = right;
        this.bubbleEl.style.top = top;
        this.bubbleEl.style.bottom = bottom;
        this.getBubbleData();
        this.arrowEl ? this.setArrow(placement) : null;
    }
    resetBubbleAttrs() {
        this.bubbleEl.style.cssText = this.bubbleEl.style.cssText.replace('margin-left:', '').replace('margin-top:', '').replace('width:', '').replace('height:', '');
        this.bubbleEl.style.left = 'auto';
        this.bubbleEl.style.right = 'auto';
        this.bubbleEl.style.top = 'auto';
        this.bubbleEl.style.bottom = 'auto';
        this.getBubbleData();
    }
    getSurroundGap() {
        let left = this.targetData.offsetLeft - this.bubbleData.width - this.gap, top = this.targetData.offsetTop - this.bubbleData.height - this.gap, right = this.browserData.width - this.targetData.offsetRight - this.bubbleData.width - this.gap, bottom = this.browserData.height - this.targetData.offsetBottom - this.bubbleData.height - this.gap, startTop = this.targetData.offsetTop, startBottom = this.browserData.height - (startTop + this.bubbleData.height), centerTop = this.targetData.offsetTop - (this.bubbleData.height - this.targetData.height) / 2, centerBottom = this.browserData.height - (centerTop + this.bubbleData.height), endTop = this.targetData.offsetBottom - this.bubbleData.height, endBottom = this.browserData.height - this.targetData.offsetBottom, startLeft = this.targetData.offsetLeft, startRight = this.browserData.width - (startLeft + this.bubbleData.width), centerLeft = this.targetData.offsetLeft - (this.bubbleData.width - this.targetData.width) / 2, centerRight = this.browserData.width - (centerLeft + this.bubbleData.width), endLeft = this.targetData.offsetRight - this.bubbleData.width, endRight = this.browserData.width - this.targetData.offsetRight;
        return {
            left, right, top, bottom,
            start: { left: startLeft, right: startRight, top: startTop, bottom: startBottom },
            center: { left: centerLeft, right: centerRight, top: centerTop, bottom: centerBottom },
            end: { left: endLeft, right: endRight, top: endTop, bottom: endBottom }
        };
    }
    fixPlacement() {
        if (this.targetData.gapRight > this.browserData.width || this.targetData.offsetLeft > this.browserData.width || ['center', 'center-max'].includes(this.placement))
            return;
        let gaps = this.getSurroundGap();
        if (this.placement.includes('top') && (gaps.top < 0 && gaps.bottom >= 0)) {
            this.placement = this.placement.replace('top', 'bottom');
        }
        else if (this.placement.includes('bottom') && (gaps.bottom < 0 && gaps.top >= 0)) {
            this.placement = this.placement.replace('bottom', 'top');
        }
        else if (this.placement.includes('left') && (gaps.left < 0 && gaps.right >= 0)) {
            this.placement = this.placement.replace('left', 'right');
        }
        else if (this.placement.includes('right') && (gaps.right < 0 && gaps.left >= 0)) {
            this.placement = this.placement.replace('right', 'left');
        }
        if (!['left-max', 'right-max', 'top-max', 'bottom-max'].includes(this.placement)) {
            if ((this.placement.includes('top') || this.placement.includes('bottom')) && this.bubbleData.width < this.browserData.width) {
                if (this.bubbleData.width > this.targetData.width) {
                    if (this.targetData.offsetLeft < 0 && this.targetData.gapRight < this.browserData.width) {
                        this.placement = this.placement.replace('end', 'start').replace('center', 'start');
                    }
                    else if (this.targetData.gapRight < 0 && this.targetData.offsetLeft < this.browserData.width) {
                        this.placement = this.placement.replace('start', 'end').replace('center', 'end');
                    }
                    else {
                        if (gaps.end.left < 0 && gaps.center.left < 0) {
                            this.placement = this.placement.replace('end', 'start').replace('center', 'start');
                        }
                        else if (gaps.end.left < 0 && this.placement.includes('end')) {
                            this.placement = this.placement.replace('end', 'center');
                        }
                        else if (gaps.end.left < 0 && gaps.center.left > 0 && this.placement.includes('start') && (this.options.placement.includes('end') || this.options.placement.includes('center'))) {
                            this.placement = this.placement.replace('start', 'center');
                        }
                        else if (gaps.end.left > 0 && this.placement.includes('center') && (this.options.placement.includes('end'))) {
                            this.placement = this.placement.replace('center', 'end');
                        }
                        if (gaps.start.right < 0 && gaps.center.right < 0) {
                            this.placement = this.placement.replace('start', 'end').replace('center', 'end');
                        }
                        else if (gaps.start.right < 0 && this.placement.includes('start')) {
                            this.placement = this.placement.replace('start', 'center');
                        }
                        else if (gaps.start.right < 0 && gaps.center.right > 0 && this.placement.includes('end') && (this.options.placement.includes('start') || this.options.placement.includes('center'))) {
                            this.placement = this.placement.replace('end', 'center');
                        }
                        else if (gaps.start.right > 0 && this.placement.includes('center') && (this.options.placement.includes('start'))) {
                            this.placement = this.placement.replace('center', 'start');
                        }
                    }
                }
                else {
                    if (gaps.start.left < 0 && gaps.center.left < 0) {
                        this.placement = this.placement.replace('start', 'end').replace('center', 'end');
                    }
                    else if (gaps.start.left < 0 && this.placement.includes('start')) {
                        this.placement = this.placement.replace('start', 'center');
                    }
                    else if (gaps.start.left < 0 && gaps.center.left > 0 && this.placement.includes('end') && (this.options.placement.includes('start') || this.options.placement.includes('center'))) {
                        this.placement = this.placement.replace('end', 'center');
                    }
                    else if (gaps.start.left > 0 && this.placement.includes('center') && (this.options.placement.includes('start'))) {
                        this.placement = this.placement.replace('center', 'start');
                    }
                    if (gaps.end.right < 0 && gaps.center.right < 0) {
                        this.placement = this.placement.replace('end', 'start').replace('center', 'start');
                    }
                    else if (gaps.end.right < 0 && this.placement.includes('end')) {
                        this.placement = this.placement.replace('end', 'center');
                    }
                    else if (gaps.end.right < 0 && gaps.center.right > 0 && this.placement.includes('start') && (this.options.placement.includes('end') || this.options.placement.includes('center'))) {
                        this.placement = this.placement.replace('start', 'center');
                    }
                    else if (gaps.end.right > 0 && this.placement.includes('center') && (this.options.placement.includes('end'))) {
                        this.placement = this.placement.replace('center', 'end');
                    }
                }
            }
            if ((this.placement.includes('left') || this.placement.includes('right')) && this.bubbleData.height < this.browserData.height) {
                if (this.bubbleData.height > this.targetData.height) {
                    if (this.targetData.offsetTop < 0 && this.targetData.gapBottom < this.browserData.height) {
                        this.placement = this.placement.replace('end', 'start').replace('center', 'start');
                    }
                    else if (this.targetData.gapBottom < 0 && this.targetData.offsetTop < this.browserData.height) {
                        this.placement = this.placement.replace('start', 'end').replace('center', 'end');
                    }
                    else {
                        if (gaps.end.top < 0 && gaps.center.top < 0) {
                            this.placement = this.placement.replace('end', 'start').replace('center', 'start');
                        }
                        else if (gaps.end.top < 0 && this.placement.includes('end')) {
                            this.placement = this.placement.replace('end', 'center');
                        }
                        else if (gaps.end.top < 0 && gaps.center.top > 0 && this.placement.includes('start') && (this.options.placement.includes('end') || this.options.placement.includes('center'))) {
                            this.placement = this.placement.replace('start', 'center');
                        }
                        else if (gaps.end.top > 0 && this.placement.includes('center') && (this.options.placement.includes('end'))) {
                            this.placement = this.placement.replace('center', 'end');
                        }
                        if (gaps.start.bottom < 0 && gaps.center.bottom < 0) {
                            this.placement = this.placement.replace('start', 'end').replace('center', 'end');
                        }
                        else if (gaps.start.bottom < 0 && this.placement.includes('start')) {
                            this.placement = this.placement.replace('start', 'center');
                        }
                        else if (gaps.start.bottom < 0 && gaps.center.bottom > 0 && this.placement.includes('end') && (this.options.placement.includes('start') || this.options.placement.includes('center'))) {
                            this.placement = this.placement.replace('end', 'center');
                        }
                        else if (gaps.start.bottom > 0 && this.placement.includes('center') && (this.options.placement.includes('start'))) {
                            this.placement = this.placement.replace('center', 'start');
                        }
                    }
                }
                else {
                    if (gaps.start.top < 0 && gaps.center.top < 0) {
                        this.placement = this.placement.replace('start', 'end').replace('center', 'end');
                    }
                    else if (gaps.start.top < 0 && this.placement.includes('start')) {
                        this.placement = this.placement.replace('start', 'center');
                    }
                    else if (gaps.start.top < 0 && gaps.center.top > 0 && this.placement.includes('end') && (this.options.placement.includes('start') || this.options.placement.includes('center'))) {
                        this.placement = this.placement.replace('end', 'center');
                    }
                    else if (gaps.start.top > 0 && this.placement.includes('center') && (this.options.placement.includes('start'))) {
                        this.placement = this.placement.replace('center', 'start');
                    }
                    if (gaps.end.bottom < 0 && gaps.center.bottom < 0) {
                        this.placement = this.placement.replace('end', 'start').replace('center', 'start');
                    }
                    else if (gaps.end.bottom < 0 && this.placement.includes('end')) {
                        this.placement = this.placement.replace('end', 'center');
                    }
                    else if (gaps.end.bottom < 0 && gaps.center.bottom > 0 && this.placement.includes('start') && (this.options.placement.includes('end') || this.options.placement.includes('center'))) {
                        this.placement = this.placement.replace('start', 'center');
                    }
                    else if (gaps.end.bottom > 0 && this.placement.includes('center') && (this.options.placement.includes('end'))) {
                        this.placement = this.placement.replace('center', 'end');
                    }
                }
            }
        }
        this.fullyExpose(gaps);
        this.setPlacementAttr();
    }
    fullyExpose(gaps) {
        if (this.options.placement === this.placement || this.placement.includes('max'))
            return;
        let placeArr = this.placement.split('-'), parentStr = placeArr[0], childStr = placeArr[1];
        parentStr = (this.options.placement.includes('top') && gaps.top >= 0) ? 'top' :
            (this.options.placement.includes('bottom') && gaps.bottom >= 0) ? 'bottom' :
                (this.options.placement.includes('left') && gaps.left >= 0) ? 'left' :
                    (this.options.placement.includes('right') && gaps.right >= 0) ? 'right' : placeArr[0];
        if (this.options.placement.includes('top') || this.options.placement.includes('bottom')) {
            if (this.options.placement.includes('start') && gaps.start[(this.bubbleData.width > this.targetData.width) ? 'right' : 'left'] >= 0) {
                childStr = 'start';
            }
            else if (this.options.placement.includes('end') && gaps.end[(this.bubbleData.width > this.targetData.width) ? 'left' : 'right'] >= 0) {
                childStr = 'end';
            }
            else if (this.options.placement.includes('center') && gaps.center.left >= 0 && gaps.center.right >= 0) {
                childStr = 'center';
            }
        }
        else if (this.options.placement.includes('left') || this.options.placement.includes('right')) {
            if (this.options.placement.includes('start') && gaps.start[(this.bubbleData.height > this.targetData.height) ? 'bottom' : 'top'] >= 0) {
                childStr = 'start';
            }
            else if (this.options.placement.includes('end') && gaps.end[(this.bubbleData.height > this.targetData.height) ? 'top' : 'bottom'] >= 0) {
                childStr = 'end';
            }
            else if (this.options.placement.includes('center') && gaps.center.top >= 0 && gaps.center.bottom >= 0) {
                childStr = 'center';
            }
        }
        this.placement = `${parentStr}-${childStr}`;
    }
    unsettable(type) {
        let el = type === 'target' ? this.targetEl : this.bubbleEl;
        return elState(el).isUncalc;
    }
    
    resetPlacement(cb) {
        if (this.destroyed || !this.places.includes(this.placement) || this.unsettable()) {
            return this;
        }
        this.getRectsData();
        this.resetBubbleAttrs();
        this.fixPlacement();
        this.setPlacement(this.placement);
        super.listen({ name: 'changed', cb, params: [this.placement] });
    }
    
    change(placement, cb) {
        if (this.destroyed) {
            return this;
        }
        placement && (this.placement = this.correctPlace(placement));
        if (!this.places.includes(this.placement))
            return this;
        this.options.placement = this.placement;
        this.setAttrs();
        this.getRectsData();
        this.resetBubbleAttrs();
        this.setPlacement(this.placement);
        super.listen({ name: 'changed', cb, params: [this.placement] });
        return this;
    }
    
    transfer(elem, cb) {
        if (this.destroyed || !elem)
            return;
        this.targetEl = getEl(elem);
        if (this.targetEl) {
            this.resetPlacement(cb);
            super.listen({ name: 'transferred', cb, params: [this.targetEl] });
        }
        return this;
    }
    
    destroy(cb) {
        if (this.destroyed)
            return;
        this.removeObs();
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb, params: [this.placement] });
        return this;
    }
}

const optHover = [
    {
        attr: 'timeout',
        prop: 'timeout',
        value: 50,
    },
    {
        attr: 'intvl',
        prop: 'intvl',
        value: 50,
    },
    {
        attr: 'throttle',
        prop: 'throttle',
        value: 200,
    },
    {
        attr: 'threshold',
        prop: 'threshold',
        value: 7,
    },
    {
        attr: 'hold',
        prop: 'hold',
        value: '',
    },
    {
        attr: 'on-move',
        prop: 'onMove',
        value: null,
    },
    {
        attr: 'on-enter',
        prop: 'onEnter',
        value: null,
    },
    {
        attr: 'on-leave',
        prop: 'onLeave',
        value: null,
    },
    ...optBase
];

class Hover extends ModBaseListen {
    options = {};
    curX;
    curY;
    preX;
    preY;
    isActive;
    enterTimer;
    leaveTimer;
    eventToggle;
    holdEl;
    enterCompare;
    globalEvent;
    moveEvent;
    enterEvent;
    leaveEvent;
    enterHold;
    leaveHold;
    static hostType = 'node';
    static optMaps = optHover;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            maps: Hover.optMaps,
            host: elem,
        });
        
        
        
        
        
        
        
        
        
        this.curX;
        this.curY;
        
        
        this.preX;
        this.preY;
        this.enterTimer;
        this.leaveTimer;
        
        this.isActive = false;
        
        this.holdEl = getEl(this.options.hold);
        this.eventToggle = (type, elem, event) => {
            let obj = { event, target: elem };
            if (type === 'enter') {
                this.isActive = true;
                super.listen({ name: 'enter', params: [{ ...obj, inside: true, name: 'enter' }] });
            }
            else if (type === 'leave') {
                this.isActive = false;
                super.listen({ name: 'leave', params: [{ ...obj, inside: false, name: 'leave' }] });
            }
        };
        this.enterCompare = (elem, event) => {
            let distX = this.preX - this.curX, distY = this.preY - this.curY, distance = Math.sqrt(distX ** 2 + distY ** 2);
            if (distance < this.options.threshold) {
                if (!this.isActive) {
                    this.eventToggle('enter', elem, event);
                }
            }
            else {
                this.preX = this.curX;
                this.preY = this.curY;
                this.enterTimer = setTimeout(() => {
                    this.enterCompare(elem, event);
                }, this.options.intvl);
            }
        };
        this.globalEvent = throttle((event) => {
            let inTarget = contains(event.target, this.targetEl), inHold = this.holdEl ? contains(event.target, this.holdEl) : true, inside = inTarget && inHold;
            super.listen({ name: 'move', params: [{ inside, event, name: 'move', target: event.target }] });
        }, this.options.throttle);
        this.moveEvent = (event) => {
            this.curX = event.pageX;
            this.curY = event.pageY;
        };
        this.enterEvent = (event) => {
            clearTimeout(this.leaveTimer);
            this.preX = event.pageX;
            this.preY = event.pageY;
            this.enterTimer = setTimeout(() => {
                this.enterCompare(event.target, event);
            }, this.options.intvl);
        };
        this.leaveEvent = (event) => {
            clearTimeout(this.enterTimer);
            this.leaveTimer = setTimeout(() => {
                this.eventToggle('leave', event.target, event);
            }, this.options.timeout);
        };
        this.enterHold = () => {
            clearTimeout(this.leaveTimer);
        };
        this.leaveHold = (event) => {
            clearTimeout(this.enterTimer);
            this.leaveTimer = setTimeout(() => {
                this.eventToggle('leave', this.targetEl, event);
            }, this.options.timeout);
        };
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        document.body.removeEventListener('mousemove', this.globalEvent);
        this.targetEl.removeEventListener('mousemove', this.moveEvent);
        this.targetEl.removeEventListener('mouseenter', this.enterEvent);
        this.targetEl.removeEventListener('mouseleave', this.leaveEvent);
        document.body.addEventListener('mousemove', this.globalEvent);
        this.targetEl.addEventListener('mousemove', this.moveEvent);
        this.targetEl.addEventListener('mouseenter', this.enterEvent);
        this.targetEl.addEventListener('mouseleave', this.leaveEvent);
        if (this.holdEl) {
            this.holdEl.removeEventListener('mouseenter', this.enterHold);
            this.holdEl.removeEventListener('mouseleave', this.leaveHold);
            this.holdEl.addEventListener('mouseenter', this.enterHold);
            this.holdEl.addEventListener('mouseleave', this.leaveHold);
        }
        super.listen({ name: 'initiated', cb });
    }
    
    destroy(cb) {
        if (this.destroyed) {
            return this;
        }
        document.body.removeEventListener('mousemove', this.globalEvent);
        this.targetEl.removeEventListener('mousemove', this.moveEvent);
        this.targetEl.removeEventListener('mouseenter', this.enterEvent);
        this.targetEl.removeEventListener('mouseleave', this.leaveEvent);
        if (this.holdEl) {
            this.holdEl.removeEventListener('mouseenter', this.enterHold);
            this.holdEl.removeEventListener('mouseleave', this.leaveHold);
        }
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

const optPopup = [
    {
        attr: 'theme',
        prop: 'theme',
        value: '',
    },
    {
        attr: 'placement',
        prop: 'placement',
        value: 'top',
    },
    {
        attr: 'trigger',
        prop: 'trigger',
        value: 'hover',
    },
    {
        attr: 'page-close',
        prop: 'pageClose',
        value: true,
    },
    {
        attr: 'size',
        prop: 'size',
        value: 'md',
    },
    {
        attr: 'in',
        prop: 'in',
        value: 'fadeIn',
    },
    {
        attr: 'out',
        prop: 'out',
        value: 'fadeOut',
    },
    {
        attr: 'multiple',
        prop: 'multiple',
        value: false,
    },
    {
        attr: 'height',
        prop: 'height',
        value: '',
    },
    {
        attr: 'adaptive',
        prop: 'adaptive',
        value: true,
    },
    {
        attr: 'position',
        prop: 'position',
        value: {},
    },
    {
        attr: 'arrow',
        prop: 'arrow',
        value: {
            enable: true,
            size: '0.5rem',
            offset: '2.8rem',
            gap: '.8rem',
        }
    },
    {
        attr: 'wing',
        prop: 'wing',
        value: {
            selector: '',
            trigger: 'click',
            actClass: '',
        },
    },
    {
        attr: 'on-reposition',
        prop: 'onReposition',
        value: null,
    },
    ...optBubble
];

const ceils = {
    xs: parseInt(getComputedVar('--_ceil-xs')) || 500,
    sm: parseInt(getComputedVar('--_ceil-sm')) || 900,
    md: parseInt(getComputedVar('--_ceil-md')) || 1200,
    lg: parseInt(getComputedVar('--_ceil-lg')) || 1500,
};

const getClientType = () => {
    let width = document.body.clientWidth, result = (width < ceils.xs) ? 'phone' :
        (width > ceils.xs && width < ceils.sm) ? 'pad' :
            (width > ceils.sm && width < ceils.md) ? 'flip' :
                (width > ceils.md && width < ceils.lg) ? 'pro' : 'pc';
    return result;
};

class Popup extends ModBaseListenCacheBubble {
    options = {};
    confirmEl;
    cancelEl;
    closeEl;
    triggerShow;
    toggleShow;
    content;
    duration;
    wrapHeight;
    bubbleType;
    aniIn;
    aniOut;
    initialPlace;
    isCompField;
    static optMaps = optPopup;
    constructor(elem, options = {}, initial = config.initial) {
        super();
        super.ready({
            options,
            host: elem,
            maps: Popup.optMaps,
            component: false,
            spread: ['arrow', 'tools', 'padding', 'footer', 'autoFill', 'bullet']
        });
        
        
        
        
        
        
        
        
        
        
        
        
        this.bubbleType = 'popup';
        let _this = this;
        this.triggerShow = function (evt) {
            evt.preventDefault();
            let isWing = _this.wings.includes(this) ? true : false, trigger = isWing ? (_this.options.wing.trigger || _this.options.trigger) : _this.options.trigger;
            if (trigger === 'click') {
                if (this === _this.targetEl && _this.options.canClick) {
                    let target = getEvtTarget(evt), tmp = _this.options.canClick.call(_this, target, evt);
                    if (!tmp)
                        return;
                }
                if (_this.state === 'hidden') {
                    _this.show();
                    if (!_this.options.multiple) {
                        let items = instance.data.filter((i) => i.type === _this.constructor.name.toLowerCase() && i.ins !== _this && i.ins.state === 'shown');
                        items.forEach((i) => {
                            i.ins.hide();
                        });
                    }
                    else {
                        _this.options.wing.actClass && _this.wings.includes(this) && this.classList.add(_this.options.wing.actClass);
                    }
                }
                else if (_this.state === 'shown') {
                    !_this.options.keepShow && _this.hide();
                }
            }
            else if (_this.options.trigger === 'input') {
                if (!_this.targetEl?.value) {
                    !_this.options.keepShow && _this.hide();
                }
                else {
                    _this.show();
                    _this.options.wing.actClass && _this.wings.includes(this) && this.classList.add(_this.options.wing.actClass);
                }
            }
        };
        this.toggleShow = (e) => {
            !contains(e.target, [this.mainEl, this.targetEl, ...this.wings]) &&
                elState(e.target).isVisible &&
                this.state === 'shown' &&
                !this.options.keepShow &&
                this.hide();
        };
        this.triggerClose = () => {
            !this.options.keepShow && this.hide();
        };
        super.listen({ name: 'constructed' });
        initial && this.targetEl && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.setEmpty();
        this.options.placement = this.options.placement || 'top';
        this.isCompField = fieldTypes.includes(this.options.contType.toLowerCase());
        if (this.isCompField) {
            this.options.tools.placement = 'outside';
        }
        
        this.wings = getEls(this.options.wing.selector);
        this.renderBubble('popup');
        this.setAttrs();
        await getContent.call(this, {
            content: this.options.content,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: {
                xhrName: 'contXhr',
                spinSel: this.contEl,
                ...this.options.ajax
            },
            request: (data) => {
                this.listen({ name: 'request', params: [data] });
            },
            cb: (data) => {
                this.renderContent(data);
            }
        });
        this.fixAni(this.options.placement);
        this.createPosition();
        this.handleFooter();
        this.handleTools();
        this.bindTrigger();
        this.contEl.querySelectorAll(`[${alias}="closebubble"]`).forEach((k) => {
            k.removeEventListener('click', this.triggerClose);
            k.addEventListener('click', this.triggerClose, false);
        });
        this.initBullets();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    fixAni(placement) {
        this.aniIn = placement.includes('max') && this.options.in === 'slideDown' ? 'fadeIn' : this.options.in;
        this.aniOut = placement.includes('max') && this.options.out === 'slideUp' ? 'fadeOut' : this.options.out;
    }
    handleTools() {
        if (this.options.tools.enable) {
            this.options.tools.children.forEach((k) => {
                if (k.name === 'close') {
                    k.wrapEl.onclick = () => this.hide();
                }
                else if (k.name === 'enlarge') {
                    if (this.positionIns.placement === 'center-max') {
                        classes(k.iconEl).replace(k.icon, k.swap);
                        this.initialPlace = this.rawOpts.placement;
                    }
                    else {
                        this.initialPlace = this.positionIns.placement;
                    }
                    k.wrapEl.onclick = () => {
                        if (this.positionIns.placement === 'center-max') {
                            classes(k.iconEl).replace(k.swap, k.icon);
                            this.reposition(this.initialPlace);
                        }
                        else {
                            classes(k.iconEl).replace(k.icon, k.swap);
                            this.reposition('center-max');
                        }
                    };
                }
                else if (k.name === 'widen') {
                    if (this.positionIns.placement === 'top-max' || this.positionIns.placement === 'bottom-max') {
                        classes(k.iconEl).replace(k.icon, k.swap);
                        this.initialPlace = this.positionIns.placement.split('-')[0];
                    }
                    else {
                        this.initialPlace = this.positionIns.placement;
                    }
                    k.wrapEl.onclick = () => {
                        if (!this.positionIns.placement.includes('top') && !this.positionIns.placement.includes('bottom')) {
                            console.warn('Only valid for placement=top-* and placement=bottom-*!');
                            return;
                        }
                        if (['left-max', 'right-max', 'top-max', 'bottom-max'].includes(this.positionIns.placement)) {
                            classes(k.iconEl).replace(k.swap, k.icon);
                            this.reposition(this.initialPlace);
                        }
                        else {
                            let position = this.positionIns.placement.split('-')[0];
                            classes(k.iconEl).replace(k.icon, k.swap);
                            this.reposition(position + '-max');
                        }
                    };
                }
                else if (k.name === 'heighten') {
                    if (this.positionIns.placement === 'left-max' || this.positionIns.placement === 'right-max') {
                        classes(k.iconEl).replace(k.icon, k.swap);
                        this.initialPlace = this.positionIns.placement.split('-')[0];
                    }
                    else {
                        this.initialPlace = this.positionIns.placement;
                    }
                    k.wrapEl.onclick = () => {
                        if (!this.positionIns.placement.includes('left') && !this.positionIns.placement.includes('right')) {
                            console.warn('Only valid for placement=left-* and placement=right-*!');
                            return;
                        }
                        if (['left-max', 'right-max', 'top-max', 'bottom-max'].includes(this.positionIns.placement)) {
                            classes(k.iconEl).replace(k.swap, k.icon);
                            this.reposition(this.initialPlace);
                        }
                        else {
                            let position = this.positionIns.placement.split('-')[0];
                            classes(k.iconEl).replace(k.icon, k.swap);
                            this.reposition(position + '-max');
                        }
                    };
                }
                else {
                    k.action && k.action.call(this, k);
                }
            });
        }
    }
    setAttrs() {
        this.options.classes && classes(this.mainEl).add(this.options.classes);
        this.options.size ? this.mainEl.setAttribute('size', this.options.size) : this.mainEl.removeAttribute('size');
        this.options.contType ? this.mainEl.setAttribute('conttype', this.options.contType) : this.mainEl.removeAttribute('conttype');
        this.options.placement ? this.mainEl.setAttribute('placement', this.options.placement) : this.mainEl.removeAttribute('placement', this.options.placement);
        this.contEl.toggleAttribute('dedicated', this.options.dedicated);
        this.options.height ? addStyle(this.contEl, 'height', this.options.height) : removeStyle(this.contEl, 'height');
        this.options.zIndex ? addStyle(this.mainEl, 'z-index', this.options.zIndex) : removeStyle(this.mainEl, 'z-index');
        this.options.tools.enable ? this.mainEl.setAttribute('toolsplace', this.options.tools.placement) : this.mainEl.removeAttribute('toolsplace');
        this.options.theme ? this.mainEl.setAttribute('theme', this.options.theme) : this.mainEl.removeAttribute('theme');
    }
    createPosition() {
        let tmpArr = this.options.placement.split('-'), placement = this.options.placement;
        if (tmpArr.length === 2 && this.options.adaptive && getClientType() === 'phone') {
            if (!tmpArr.includes('max') && !['top', 'bottom'].includes(tmpArr[0])) {
                placement = 'top-max';
            }
            else if (!tmpArr.includes('max') && ['top', 'bottom'].includes(tmpArr[0])) {
                placement = tmpArr[0] + '-max';
            }
        }
        
        this.positionIns = new Position(this.targetEl, this.mainEl, extend({
            target: {
                placement,
                arrow: this.options.arrow,
                targetObs: {
                    attrs: [],
                },
                bubbleObs: {
                    attrs: [],
                },
                delay: 0,
            },
            source: this.options.position
        }));
    }
    bindTrigger() {
        if (this.options.trigger === 'click' || this.options.trigger === 'input') {
            if (!this.canTrigger) {
                this.targetEl.removeEventListener(this.options.trigger, this.triggerShow);
                this.targetEl.addEventListener(this.options.trigger, this.triggerShow, false);
            }
            if (this.wings.length > 0 && !this.canTrigger) {
                this.wings.forEach((item) => {
                    let trigger = this.options.wing.trigger || this.options.trigger;
                    item.removeEventListener(trigger, this.triggerShow);
                    item.addEventListener(trigger, this.triggerShow, false);
                });
            }
        }
        else if (this.options.trigger === 'hover') {
            
            this.hoverIns = new Hover(this.targetEl, {
                onEnter: () => {
                    this.show();
                },
                onLeave: () => {
                    !this.options.keepShow && this.hide();
                },
                hold: this.mainEl,
            });
        }
        else if (this.options.trigger === 'sticky') {
            this.show();
        }
        if (this.options.pageClose && !this.options.multiple) {
            !this.canTrigger && document.addEventListener('click', this.toggleShow, false);
        }
        this.canTrigger = true;
    }
    unbindTrigger(mode = 'host') {
        this.canTrigger = false;
        if (this.options.trigger === 'click' || this.options.trigger === 'input') {
            this.targetEl.removeEventListener(this.options.trigger, this.triggerShow);
            if (this.wings.length > 0 && mode === 'all') {
                this.wings.forEach((item) => {
                    let trigger = this.options.wing.trigger || this.options.trigger;
                    item.removeEventListener(trigger, this.triggerShow);
                });
            }
        }
        else if (this.options.trigger === 'hover') {
            this.hoverIns.destroy();
        }
        if (this.options.pageClose && !this.options.multiple && mode === 'all') {
            document.removeEventListener('click', this.toggleShow, false);
        }
    }
    
    reposition(placement, cb) {
        if (this.destroyed || !placement) {
            return this;
        }
        this.positionIns.change(placement, (placement) => {
            this.fixAni(placement);
            this.updateCache({ placement });
            super.listen({ name: 'reposition', cb, params: [placement] });
        });
        return this;
    }
    
    async show(cb) {
        if (this.destroyed || this.state !== 'hidden' || this.options.asleep) {
            return this;
        }
        this.state = 'ing';
        this.options.b4Show && await this.options.b4Show.call(this);
        elState(this.mainEl).isVirtual && document.body.appendChild(this.mainEl);
        super.listen({ name: 'show', cb });
        this.wrapHeight = parseInt(style(this.wrapEl).height);
        this.duration = this.options.duration || (this.options.autoDur ? getAutoDur(this.wrapHeight) : parseFloat(style(this.mainEl).animationDuration) * 1000);
        this.positionIns.change();
        this.mainEl.setAttribute('visibility', 'visible');
        this.targetEl && this.targetEl.classList.add(this.options.actClass);
        if (this.aniIn === 'slideDown') {
            easeHeight({ el: this.wrapEl, height: this.wrapHeight, type: 'down', duration: this.duration });
        }
        else {
            this.options.duration && (this.mainEl.style.animationDuration = `${this.options.duration}ms`);
            this.aniIn && (this.mainEl.style.animationName = prefix + this.aniIn);
        }
        this.lastShowTime = Date.now();
        this.hoverIns && (this.hoverIns.isActive = true);
        await delay({
            duration: this.duration,
            done: () => {
                this.state = 'shown';
                super.listen({ name: 'shown', cb });
            }
        });
        return this;
    }
    
    async hide(cb) {
        if (this.destroyed || this.state !== 'shown') {
            return this;
        }
        this.state = 'ing';
        if (!this.options.deadShow) {
            this.options.b4Hide && await this.options.b4Hide.call(this);
            super.listen({ name: 'hide', cb });
            this.targetEl && this.targetEl.classList.remove(this.options.actClass);
            this.options.wing.actClass && this.wings.map((k) => { k.classList.remove(this.options.wing.actClass); });
            if (this.aniOut === 'slideUp') {
                easeHeight({ el: this.wrapEl, height: this.wrapHeight, type: 'up', duration: this.duration });
            }
            else {
                this.aniOut && (this.mainEl.style.animationName = prefix + this.aniOut);
            }
            this.mainEl.querySelectorAll('video,audio').forEach((k) => {
                k.pause();
            });
            this.hoverIns && (this.hoverIns.isActive = false);
            await delay({
                duration: this.duration,
                done: () => {
                    this.state = 'hidden';
                    this.mainEl.setAttribute('visibility', 'hidden');
                    this.mainEl.remove();
                    super.listen({ name: 'hidden', cb });
                    this.aniOut === 'slideUp' && (this.wrapEl.style.height = 'auto');
                }
            });
        }
        return this;
    }
}

const optDodge = [
    {
        attr: 'placeholder',
        prop: 'placeholder',
        value: '',
    },
    {
        attr: 'on-setted',
        prop: 'onSetted',
        value: null,
    },
    {
        attr: 'on-canceled',
        prop: 'onCanceled',
        value: null,
    },
    ...optBase
];

class Dodge extends ModBaseListenCache {
    options = {};
    inputEl;
    labelEl;
    isFocus;
    toggle;
    timestamp;
    placeholder;
    static hostType = 'node';
    static optMaps = optDodge;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            host: elem,
            maps: Dodge.optMaps,
        });
        
        
        
        
        
        
        
        
        
        
        
        
        this.timestamp = Date.now();
        
        this.inputEl = this.targetEl.querySelector("input,textarea");
        if (!this.inputEl) {
            throw new Error('Form field node not found!');
        }
        
        this.labelEl = null;
        
        this.isFocus = false;
        this.toggle = {
            focus: () => {
                this.inputEl.focus();
                this.inputEl.removeAttribute('readonly');
                this.targetEl.setAttribute('dodged', '');
                this.isFocus = true;
            },
            blur: (e) => {
                this.inputEl.blur();
                this.inputEl.setAttribute('readonly', '');
                !this.inputEl.value ? this.targetEl.removeAttribute('dodged') : this.targetEl.setAttribute('dodged', '');
                this.isFocus = false;
            }
        };
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        
        this.placeholder = this.getPlaceholder();
        this.setAttrs();
        this.render();
        this.inputEl.addEventListener('focus', this.toggle.focus, false);
        this.inputEl.addEventListener('blur', this.toggle.blur, false);
        this.labelEl.onclick = () => {
            this.toggle.focus();
        };
        this.inputEl.value ? this.toggle.focus() : null;
        super.listen({ name: 'initiated', cb });
        return this;
    }
    getPlaceholder() {
        let inputHolder = this.inputEl.getAttribute('placeholder');
        return this.options.placeholder ? this.options.placeholder :
            inputHolder ? inputHolder : config.lang.form.placeholder;
    }
    render() {
        if (!this.labelEl) {
            this.labelEl = createEl('label', '', this.placeholder);
            this.inputEl.removeAttribute("placeholder");
            this.inputEl.insertAdjacentElement('afterend', this.labelEl);
        }
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}dodge`);
        this.inputEl.toggleAttribute('readonly', true);
        this.inputEl.classList.contains("ax-lg") ? this.targetEl.classList.add("ax-lg") : null;
    }
    
    set(value, cb) {
        if (this.destroyed) {
            return this;
        }
        if (value) {
            this.inputEl.value = value;
            this.toggle.focus();
        }
        else {
            this.inputEl.focus();
        }
        super.listen({ name: 'set', cb, params: [value] });
        return this;
    }
    
    cancel(cb) {
        if (this.destroyed) {
            return this;
        }
        this.inputEl.value = '';
        this.inputEl.blur();
        this.toggle.blur();
        super.listen({ name: 'canceled', cb });
        return this;
    }
    
    destroy(cb) {
        if (this.destroyed) {
            return this;
        }
        this.labelEl.onclick = null;
        this.inputEl.removeEventListener('focus', this.toggle.focus);
        this.inputEl.removeEventListener('blur', this.toggle.blur);
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

const optValid = [
    {
        attr: 'label',
        prop: 'label',
        value: '',
    },
    {
        attr: 'placement',
        prop: 'placement',
        value: '',
    },
    {
        attr: 'icon-show',
        prop: 'iconShow',
        value: true,
    },
    {
        attr: 'type',
        prop: 'type',
        value: 'required',
    },
    {
        attr: 'extend',
        prop: 'extend',
        value: {},
    },
    {
        attr: 'parent',
        prop: 'parent',
        value: '',
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'fail',
        prop: 'fail',
        value: '',
    },
    {
        attr: 'succ',
        prop: 'succ',
        value: '',
    },
    {
        attr: 'separator',
        prop: 'separator',
        value: ',',
    },
    {
        attr: 'style-host',
        prop: 'styleHost',
        value: false,
    },
    {
        attr: 'succ-show',
        prop: 'succShow',
        value: true,
    },
    {
        attr: 'node-name',
        prop: 'nodeName',
        value: 'div',
    },
    {
        attr: 'message',
        prop: 'message',
        value: {},
    },
    {
        attr: 'popup',
        prop: 'popup',
        value: {},
    },
    {
        attr: 'reg-local',
        prop: 'regLocal',
        value: '',
    },
    {
        attr: 'reg-chars',
        prop: 'regChars',
        value: '',
    },
    {
        attr: 'length-str',
        prop: 'lengthStr',
        value: 6,
    },
    {
        attr: 'trigger',
        prop: 'trigger',
        value: 'blur',
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'on-trigger',
        prop: 'onTrigger',
        value: null,
    },
    {
        attr: 'on-changed',
        prop: 'onChanged',
        value: null,
    },
    {
        attr: 'on-shown',
        prop: 'onShown',
        value: null
    },
    {
        attr: 'on-hidden',
        prop: 'onHidden',
        value: null
    },
    ...optBase
];

class Valid extends ModBaseListenCache {
    options = {};
    useRules;
    dftRules;
    inputEl;
    timestamp;
    name;
    label;
    parentEl;
    valid;
    hostValid;
    msgEl;
    otherBox;
    checkFun;
    types;
    userRules;
    result;
    output;
    msgIcon;
    msgText;
    popupIns;
    msgIns;
    obsProps;
    observer;
    static hostType = 'node';
    static optMaps = optValid;
    constructor(elem, options = {}, initial = config.initial) {
        super();
        super.ready({
            options,
            host: elem,
            maps: Valid.optMaps,
        });
        this.timestamp = Date.now();
        if (!this.targetEl.getAttribute('name')) {
            throw new Error('The element to be verified must have name attribute!');
        }
        
        this.name = this.targetEl.getAttribute('name');
        
        this.label = this.options.label || this.name;
        
        this.parentEl = getEl(this.options.parent) || this.targetEl.closest('form') || document.body;
        
        this.valid = { target: this.targetEl, name: this.name, ins: this };
        this.hostValid = null;
        this.pushValids();
        
        this.createMsgEl();
        this.otherBox = getEl(this.options.placement);
        this.createObs();
        this.checkFun = () => {
            this.do();
        };
        this.dftRules = {
            
            'required': (data) => {
                let fail = renderTpl(this.options.lang.required, { label: data.label });
                return !isEmpty(data.value) ? { passed: true } : { passed: false, fail, type: 'required' };
            },
            
            ...this.loopTestRules(['email', 'cellphone', 'landline', 'ip', 'id', 'zip', 'url', 'locale', 'password', 'letter', 'string', 'ymdhms', 'ymd', 'hms', 'ym', 'y', 'm', 'd', 'integer', 'number']),
            'plate': (data) => {
                let regex = data.value.length === 8 ? regExps.plate.nev : regExps.plate.oil;
                return this.testRegex({ regex, value: data.value, name: 'plate' });
            },
            'date': (data) => {
                let flag = isDateStr(data.value), fail = renderTpl(this.options.lang['date'], { label: data.label, value: data.value });
                return flag ? { passed: true } : { passed: false, fail, type: 'date' };
            },
            
            ...this.loopDateRules(['date=', 'date<', 'date<=', 'date>', 'date>=', 'date><', 'date><=', 'date>=<', 'date>=<=']),
            
            ...this.loopThanRules(['than=', 'than>', 'than>=', 'than<', 'than<=', 'than><', 'than><=', 'than>=<', 'than>=<=',]),
            
            ...this.loopLengthRules(['length=', 'length>', 'length>=', 'length<', 'length<=', 'length><', 'length><=', 'length>=<', 'length>=<=']),
            
            ...this.loopIncludeRules(['include', 'exclude']),
            
            ...this.loopValuesRules(['count=', 'count>', 'count>=', 'count<', 'count<=', 'count><', 'count><=', 'count>=<', 'count>=<=']),
            
            ...this.loopSameRules(['same', 'different']),
            
            'strength': (data) => {
                if (typeof data.value !== 'string') {
                    return { passed: false, fail: this.options.lang.strFormat };
                }
                let strength = this.getStrength(data.value), fail = renderTpl(this.options.lang['strength'], { label: data.label, name: data.name, value: strength, data: this.types['strength'] });
                return strength >= this.types['strength'] ? { passed: true } : { passed: false, fail, type: 'strength' };
            },
            
            'specific': (data) => {
                if (typeof data.value !== 'string') {
                    return { passed: false, fail: this.options.lang.strFormat };
                }
                let parse = validTools.parseSpecific(this.types['specific'], { label: data.label, name: data.name, value: data.value, tpl: this.options.lang['specific'] }), regExp = parse.regex, regText = parse.text;
                return validTools.test(data.value, regExp) ? { passed: true } : { passed: false, fail: regText, type: 'specific' };
            },
            
            'combine': (data) => {
                if (typeof data.value !== 'string') {
                    return { passed: false, fail: this.options.lang.strFormat };
                }
                let parse = validTools.parseCombine(this.types['combine'], { label: data.label, name: data.name, ins: this, value: data.value, tpl: this.options.lang['combine'] }), regExp = parse.regex, regText = parse.text;
                return validTools.test(data.value, regExp) ? { passed: true } : { passed: false, fail: regText, type: 'combine' };
            },
            
            'async': (data) => {
                return ajax.call(this, Object.assign({
                    url: this.types['async'],
                    data: {
                        label: data.label,
                        name: data.name,
                        value: data.value,
                        strength: this.getStrength(data.value),
                    },
                }, this.options.ajax));
            },
            ...this.options.extend
        };
        validTools.listenSubmit({ item: this.parentEl });
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.setEmpty();
        
        this.types = typeof this.options.type === 'string' ? paramToJson(this.options.type) : this.options.type;
        this.getUserRules();
        if (['change', 'blur', 'input'].includes(this.options.trigger)) {
            this.targetEl.removeEventListener(this.options.trigger, this.checkFun);
            this.targetEl.addEventListener(this.options.trigger, this.checkFun, false);
        }
        else if (this.options.trigger === 'load') {
            this.do();
        }
        super.listen({ name: 'initiated', cb });
        return this;
    }
    setEmpty() {
        this.msgEl ? this.msgEl.innerHTML = '' : null;
        this.popupIns ? this.popupIns.destroy : null;
        this.msgIns ? this.msgIns.destroy : null;
    }
    loopTestRules(types) {
        let result = {};
        types.forEach((k) => {
            if (regExps.hasOwnProperty(k)) {
                result[k] = (data) => {
                    if (Array.isArray(data.value)) {
                        return { passed: false, fail: this.options.lang.strFormat, id: 'strFormat' };
                    }
                    return this.testRegex({ regex: regExps[k], value: data.value, name: k });
                };
            }
        });
        return result;
    }
    loopIncludeRules(types) {
        let result = {};
        types.forEach((k) => {
            result[k] = (data) => {
                if (!Array.isArray(data.data)) {
                    return { passed: false, fail: this.options.lang.arrFormat };
                }
                let value = Array.isArray(data.value) ? data.value : data.value.split(this.options.separator), flag = isSubset(value, data.data), fail = renderTpl(this.options.lang[k], { label: data.label, name: data.name, value: data.value, data: data.data });
                k === 'exclude' && (flag = !flag);
                return flag ? { passed: true } : { passed: false, fail, type: k };
            };
        });
        return result;
    }
    loopSameRules(types) {
        let result = {};
        types.forEach((k) => {
            result[k] = (data) => {
                if (!Array.isArray(data.data)) {
                    return { passed: false, fail: this.options.lang.arrFormat };
                }
                let referField = this.parentEl.querySelector(`[name="${this.types[k][0]}"]`), referValue = this.getVals(referField, 'string'), targetValue = this.getVals(this.targetEl, 'string'), fail = renderTpl(this.options.lang[k], { label: data.label, name: data.name, value: targetValue, data: data.data }), condition = k === 'same' ? (referValue === targetValue) : (referValue !== targetValue);
                return condition ? { passed: true } : { passed: false, fail, type: k };
            };
        });
        return result;
    }
    loopDateRules(types) {
        let result = {};
        types.forEach((k) => {
            result[k] = (data) => {
                if (Array.isArray(data.value)) {
                    return { passed: false, fail: this.options.lang.strFormat };
                }
                let flag = validTools.expired(data.value, data.data).join(''), fail = renderTpl(this.options.lang[k], { label: data.label, value: data.value, data: data.data }), condition = k === 'date=' ? (flag === '0-2') :
                    k === 'date<' ? (flag === '-1-2') :
                        k === 'date<=' ? (flag === '0-2' || flag === '-1-2') :
                            k === 'date>' ? (flag === '1-2') :
                                k === 'date>=' ? (flag === '1-2' || flag === '0-2') :
                                    k === 'date><' ? (flag === '1-1') :
                                        k === 'date><=' ? (flag === '1-1' || flag === '10') :
                                            k === 'date>=<' ? (flag === '1-1' || flag === '0-1') :
                                                k === 'date>=<=' ? (['1-1', '10', '0-1', '00'].includes(flag)) : true;
                return condition ? { passed: true } : { passed: false, fail, type: k };
            };
        });
        return result;
    }
    loopThanRules(types) {
        let result = {};
        types.forEach((k) => {
            result[k] = (data) => {
                if (Array.isArray(data.value)) {
                    return { passed: false, fail: this.options.lang.strFormat };
                }
                let flag = validTools.than(data.value, data.data).join(''), fail = renderTpl(this.options.lang[k], { label: data.label, value: data.value, data: data.data }), condition = k === 'than>' ? (flag === '1-2') :
                    k === 'than>=' ? (flag === '1-2' || flag === '0-2') :
                        k === 'than<' ? (flag === '-1-2') :
                            k === 'than<=' ? (flag === '0-2' || flag === '-1-2') :
                                k === 'than=' ? (flag === '0-2') :
                                    k === 'than><' ? (flag === '1-1') :
                                        k === 'than><=' ? (flag === '1-1' || flag === '10') :
                                            k === 'than>=<' ? (flag === '1-1' || flag === '0-1') :
                                                k === 'than>=<=' ? (['1-1', '10', '0-1', '00'].includes(flag)) : true;
                return condition ? { passed: true } : { passed: false, fail, type: k };
            };
        });
        return result;
    }
    loopLengthRules(types) {
        let result = {};
        types.forEach((k) => {
            result[k] = (data) => {
                if (Array.isArray(data.value)) {
                    return { passed: false, fail: this.options.lang.strFormat, id: 'strFormat' };
                }
                let pattern = k === 'length=' ? this.types[k] :
                    k === 'length>' ? `${this.types[k] + 1},` :
                        k === 'length>=' ? `${this.types[k]},` :
                            k === 'length<' ? `0,${this.types[k] - 1}` :
                                k === 'length<=' ? `0,${this.types[k]}` :
                                    k === 'length><' ? `${this.types[k][0] + 1},${this.types[k][1] - 1}` :
                                        k === 'length><=' ? `${this.types[k][0] + 1},${this.types[k][1]}` :
                                            k === 'length>=<' ? `${this.types[k][0]},${this.types[k][1] - 1}` :
                                                k === 'length>=<=' ? `${this.types[k][0]},${this.types[k][1]}` : '', regex = new RegExp(`^.{${pattern}}$`);
                return this.testRegex({ regex, value: data.value, name: k });
            };
        });
        return result;
    }
    loopValuesRules(types) {
        let result = {};
        for (let k of types) {
            result[k] = (data) => {
                let value = (Array.isArray(data.value)) ? data.value : data.value.split(this.options.separator).filter(Boolean), fail = renderTpl(this.options.lang[k], { label: data.label, name: data.name, value: value.length, data: data.data }), condition = k === 'count>=<=' ? (value.length >= this.types[k][0] && value.length <= this.types[k][1]) :
                    k === 'count><=' ? (value.length > this.types[k][0] && value.length <= this.types[k][1]) :
                        k === 'count>=<' ? (value.length >= this.types[k][0] && value.length < this.types[k][1]) :
                            k === 'count><' ? (value.length > this.types[k][0] && value.length < this.types[k][1]) :
                                k === 'count=' ? `${value.length} === ${this.types[k]}` : `${value.length} ${k.replace('count', '')} ${this.types[k]}`;
                return new Function(`"use strict";return ${condition}`)() ? { passed: true } : { passed: false, fail, type: k };
            };
        }
        return result;
    }
    testRegex({ regex, value, name }) {
        let flag = validTools.test(value, regex), fail = renderTpl(this.options.lang[name], { label: this.label, value, data: this.types[name] });
        ((name === 'length<' || name === 'length<=') && !value) && (flag = true);
        return flag ? { passed: true } : { passed: false, fail, type: name };
    }
    extendRules(rules) {
        Object.assign(this.dftRules, rules);
    }
    getUserRules() {
        this.userRules = {};
        for (let k in this.types) {
            if (this.types.hasOwnProperty(k) && this.dftRules.hasOwnProperty(k)) {
                this.userRules[k] = this.dftRules[k];
                this.userRules[k].data = this.types[k];
            }
        }
    }
    
    getVals(field = this.targetEl, format = '') {
        return fieldTools.getVals({ target: field, parent: this.parentEl, format });
    }
    
    getStrength(value = this.getVals(this.targetEl)) {
        return validTools.getStrength(value, {
            chars: `[${this.options.regChars}]`,
            local: `[${this.options.regLocal}]`,
            length: this.options.lengthStr
        });
    }
    
    do() {
        if (this.destroyed)
            return this;
        let value = this.getVals(this.targetEl) || '';
        
        return validTools.factory({
            value,
            name: this.name,
            label: this.label,
            ins: this,
            rules: this.userRules,
            text: {
                succ: this.options.succ,
                fail: this.options.fail,
            },
            cb: (resp) => {
                
                this.result = resp;
                
                this.output = { ...resp, name: this.name, label: this.label, value, ins: this, strength: this.getStrength(value) };
                super.listen({ name: 'trigger', params: [this.output] });
                this.observer.proxy.passed = resp.passed;
                this.observer.proxy.type = resp.type;
            }
        });
    }
    toggleStyle(passed) {
        if (passed) {
            this.msgEl.classList.remove(`${prefix}c-error`);
            this.msgEl.classList.add(`${prefix}c-succ`);
            this.targetEl.setAttribute('validate', 'passed');
            if (this.options.styleHost) {
                this.targetEl.classList.remove(`${prefix}c-error`);
                this.targetEl.classList.add(`${prefix}c-succ`);
            }
        }
        else {
            this.msgEl.classList.remove(`${prefix}c-succ`);
            this.msgEl.classList.add(`${prefix}c-error`);
            this.targetEl.setAttribute('validate', 'failed');
            if (this.options.styleHost) {
                this.targetEl.classList.remove(`${prefix}c-succ`);
                this.targetEl.classList.add(`${prefix}c-error`);
            }
        }
    }
    render(msg) {
        this.toggleStyle(msg.passed);
        if (msg.passed) {
            this.msgIcon = this.options.iconShow ? icons.font["succ-f"] : '';
            this.msgText = msg.succ;
        }
        else {
            this.msgIcon = this.options.iconShow ? icons.font["error-f"] : '';
            this.msgText = msg.fail;
        }
        this.msgEl.innerHTML = this.msgIcon + this.msgText;
        if (this.otherBox) {
            elState(this.msgEl).isVirtual && this.otherBox.appendChild(this.msgEl);
        }
        else {
            if (typeof this.options.placement !== 'string') {
                return this;
            }
            this.options.placement = this.options.placement.trim();
            if (this.options.placement === 'down') {
                let box = this.targetEl.closest(`.${prefix}box-input`)?.querySelector(`[${alias}="input"]`);
                box ? box.insertAdjacentElement('afterEnd', this.msgEl) : null;
            }
            else if (this.options.placement === 'right') {
                let box = this.targetEl.closest(`.${prefix}box-input`)?.querySelector(`[${alias}="help"]`);
                box ? box.appendChild(this.msgEl) : null;
            }
            else if (this.options.placement === 'popup') {
                if (!this.popupIns) {
                    this.popupIns = new Popup(this.targetEl, {
                        content: this.msgEl,
                        placement: 'bottom-start',
                        keepShow: true,
                        footer: false,
                        tools: false,
                        arrow: {
                            enable: false,
                            gap: 0,
                        },
                        classes: `${prefix}plain`,
                        trigger: 'sticky'
                    });
                }
            }
            else if (this.options.placement === 'message') {
                if (!this.msgIns) {
                    this.msgIns = new Message({
                        content: this.msgText,
                        iconShow: true,
                        status: msg.passed ? 'succ' : 'error',
                    }).show();
                }
                else {
                    this.msgIns.update({
                        content: this.msgText,
                        status: msg.passed ? 'succ' : 'error',
                    }).show();
                }
            }
            else {
                elState(this.msgEl).isVirtual ? this.targetEl.insertAdjacentElement('afterend', this.msgEl) : null;
            }
        }
    }
    createMsgEl() {
        
        this.msgEl = this.hostValid ? this.hostValid.ins.msgEl : createEl(this.options.nodeName, { class: `${prefix}valid` });
    }
    createObs() {
        
        this.obsProps = this.hostValid ? this.hostValid.ins.obsProps : {};
        this.observer = new Observe(this.obsProps);
        this.observer.on('completed', () => {
            this.render(this.result);
            super.listen({ name: 'changed', params: [this.output] });
        });
    }
    pushValids() {
        if (ax.valids.length === 0) {
            ax.valids.push({ parent: this.parentEl, children: [this.valid] });
        }
        else {
            let index = ax.valids.findIndex(k => (k.parent === this.parentEl));
            if (index > -1) {
                let valids = ax.valids[index].children, temp = valids.find((k) => k.name === this.name);
                !temp ? valids.push(this.valid) : (this.hostValid = temp);
            }
            else {
                ax.valids.push({ parent: this.parentEl, children: [this.valid] });
            }
        }
    }
    
    hide(cb) {
        if (this.destroyed) {
            return this;
        }
        if (this.options.placement === 'message') ;
        else if (this.options.placement === 'popup') {
            this.popupIns.hide();
        }
        else {
            this.msgEl.remove();
        }
        super.listen({ name: 'hidden', cb });
        return this;
    }
    show(cb) {
        if (this.destroyed) {
            return this;
        }
        if (this.otherBox) {
            elState(this.msgEl).isVirtual ? this.otherBox.appendChild(this.msgEl) : null;
        }
        else {
            if (this.options.placement === 'message') {
                this.msgIns.show();
            }
            else if (this.options.placement === 'popup') {
                this.popupIns.show();
            }
            else {
                elState(this.msgEl).isVirtual ? this.targetEl.insertAdjacentElement('afterend', this.msgEl) : null;
            }
        }
        super.listen({ name: 'shown', cb });
        return this;
    }
    
    destroy(cb) {
        if (this.destroyed) {
            return this;
        }
        if (['change', 'blur', 'input'].includes(this.options.trigger)) {
            this.targetEl.removeEventListener(this.options.trigger, this.checkFun);
        }
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

let AXTMP_rootStart$1 = config.rootStart, AXTMP_idStart$1 = config.idStart, AXTMP_floorStart$1 = config.floorStart, AXTMP_pathHyphen$1 = config.pathHyphen;
const optMenu$1 = [
    {
        attr: 'theme',
        prop: 'theme',
        value: 'light',
    },
    {
        attr: 'content',
        prop: 'content',
        value: '',
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'passive',
        prop: 'passive',
        value: false,
    },
    {
        attr: 'expose',
        prop: 'expose',
        value: false,
    },
    {
        attr: 'active',
        prop: 'active',
        value: '',
    },
    {
        attr: 'disable',
        prop: 'disable',
        value: '',
    },
    {
        attr: 'multiple',
        prop: 'multiple',
        value: false,
    },
    {
        attr: 'expand-all',
        prop: 'expandAll',
        value: false,
    },
    {
        attr: 'z-index',
        prop: 'zIndex',
        value: 0,
    },
    {
        attr: 'trigger',
        prop: 'trigger',
        value: 'click',
    },
    {
        attr: 'root-start',
        prop: 'rootStart',
        value: AXTMP_rootStart$1,
    },
    {
        attr: 'id-start',
        prop: 'idStart',
        value: AXTMP_idStart$1,
    },
    {
        attr: 'floor-start',
        prop: 'floorStart',
        value: AXTMP_floorStart$1,
    },
    {
        attr: 'path-hyphen',
        prop: 'pathHyphen',
        value: AXTMP_pathHyphen$1,
    },
    {
        attr: 'duration',
        prop: 'duration',
        value: 0,
    },
    {
        attr: 'page-close',
        prop: 'pageClose',
        value: false,
    },
    {
        attr: 'arrow',
        prop: 'arrow',
        value: {
            enable: true,
            icon: '',
        },
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'linkage',
        prop: 'linkage',
        value: true,
    },
    {
        attr: 'unpadded',
        prop: 'unpadded',
        value: false,
    },
    {
        attr: 'lamp',
        prop: 'lamp',
        value: '',
    },
    {
        attr: 'full',
        prop: 'full',
        value: false,
    },
    {
        attr: 'spill',
        prop: 'spill',
        value: false,
    },
    {
        attr: 'layout',
        prop: 'layout',
        value: 'indent|legend|icon|disk|cube|image|label|custom|tips||badge|arrow',
    },
    {
        attr: 'tree',
        prop: 'tree',
        value: {},
    },
    {
        attr: 'drawer',
        prop: 'drawer',
        value: {
            host: '',
            placement: 'left',
            footer: false,
        },
    },
    {
        attr: 'nav',
        prop: 'nav',
        value: {
            enable: false,
            headWidth: '',
            bodyWidth: '',
            marginLeft: '',
            marginRight: '',
            align: 'left',
        },
    },
    {
        attr: 'on-activated',
        prop: 'onActivated',
        value: null
    },
    {
        attr: 'on-updatedcont',
        prop: 'onUpdatedCont',
        value: null
    },
    ...optBase
];

let AXTMP_connector = config.labelHyphen, AXTMP_separator$4 = config.splitHyphen, AXTMP_rootStart = config.rootStart, AXTMP_idStart = config.idStart, AXTMP_floorStart = config.floorStart, AXTMP_pathHyphen = config.pathHyphen;
const optTree = [
    {
        attr: 'name',
        prop: 'name',
        value: '',
    },
    {
        attr: 'value',
        prop: 'value',
        value: '',
    },
    {
        attr: 'content',
        prop: 'content',
        value: '',
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'passive',
        prop: 'passive',
        value: false,
    },
    {
        attr: 'disable',
        prop: 'disable',
        value: '',
    },
    {
        attr: 'readonly',
        prop: 'readonly',
        value: '',
    },
    {
        attr: 'min',
        prop: 'min',
        value: 0,
    },
    {
        attr: 'max',
        prop: 'max',
        value: 0,
    },
    {
        attr: 'sliced',
        prop: 'sliced',
        value: true,
    },
    {
        attr: 'expand',
        prop: 'expand',
        value: {
            value: '',
            all: false,
            only: true,
            linkage: false,
        },
    },
    {
        attr: 'select',
        prop: 'select',
        value: {
            enable: true,
            only: true,
            value: '',
            linkage: false,
            span: 'tree',
            addSelected: false,
        },
    },
    {
        attr: 'arrow',
        prop: 'arrow',
        value: {
            enable: true,
            show: `${prefix}icon-right`,
            hide: `${prefix}icon-right`,
            anim: `${prefix}rotate90`,
            type: 'icon',
            trigger: 'click',
        },
    },
    {
        attr: 'legend',
        prop: 'legend',
        value: {
            enable: false,
            parent: [`${prefix}icon-folder`, `${prefix}icon-folder-open`],
            child: `${prefix}icon-file-text`,
            type: 'icon',
        },
    },
    {
        attr: 'check',
        prop: 'check',
        value: {
            enable: false,
            type: 'checkbox',
            value: '',
            min: 0,
            max: 0,
            sliced: true,
            linkage: true,
            only: false,
            span: 'tree',
        },
    },
    {
        attr: 'tools',
        prop: 'tools',
        value: {
            enable: false,
            trigger: 'hover',
            children: ['folder', 'file', 'edit', 'remove'],
        },
    },
    {
        attr: 'output',
        prop: 'output',
        value: {
            enable: true,
            target: '',
            connector: AXTMP_connector,
            separator: AXTMP_separator$4,
            type: '',
            from: 'selected',
            field: 'label',
            instant: true,
            autosort: false,
        },
    },
    {
        attr: 'sql-data',
        prop: 'sqlData',
        value: {
            deep: 0,
            start: 0,
            count: 0,
            order: 'id asc',
            where: '',
        },
    },
    {
        attr: 'paginated',
        prop: 'paginated',
        value: {
            enable: false,
            count: 10,
            override: false,
            exception: false,
        },
    },
    {
        attr: 'delay',
        prop: 'delay',
        value: 100,
    },
    {
        attr: 'deferred',
        prop: 'deferred',
        value: false,
    },
    {
        attr: 'shortcut',
        prop: 'shortcut',
        value: {
            enable: false,
            span: 'blank',
            mean: 'selected',
        },
    },
    {
        attr: 'search',
        prop: 'search',
        value: {
            target: '',
            trigger: 'input',
            delay: 500,
            value: '',
            limit: 0,
            fuzzy: true,
            ignore: true,
        },
    },
    {
        attr: 'drag',
        prop: 'drag',
        value: {
            enable: false,
            exclude: '',
        },
    },
    {
        attr: 'drop',
        prop: 'drop',
        value: {
            attr: 'dropnode',
            global: false,
        },
    },
    {
        attr: 'chain',
        prop: 'chain',
        value: false,
    },
    {
        attr: 'auto-fill',
        prop: 'autoFill',
        value: true,
    },
    {
        attr: 'root-start',
        prop: 'rootStart',
        value: AXTMP_rootStart,
    },
    {
        attr: 'id-start',
        prop: 'idStart',
        value: AXTMP_idStart,
    },
    {
        attr: 'floor-start',
        prop: 'floorStart',
        value: AXTMP_floorStart,
    },
    {
        attr: 'path-hyphen',
        prop: 'pathHyphen',
        value: AXTMP_pathHyphen,
    },
    {
        attr: 'duration',
        prop: 'duration',
        value: 0,
    },
    {
        attr: 'bubble',
        prop: 'bubble',
        value: {
            enable: false,
            type: 'popup',
        },
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'display',
        prop: 'display',
        value: 'inline',
    },
    {
        attr: 'layout',
        prop: 'layout',
        value: 'indent|arrow|check|legend|icon|disk|cube|image|label|badge|tips|custom|tools',
    },
    {
        attr: 'feature',
        prop: 'feature',
        value: {
            type: '',
            expand: {
                all: true,
                only: false,
            },
            check: {
                linkage: true,
                only: false,
                span: 'tree',
            },
            layout: {
                dropdown: 'indent|arrow|icon|disk|cube|image|label|holder|tips|badge',
                select: 'indent|arrow|icon|disk|cube|image|label|holder|tips|badge',
                check: 'indent|arrow|check|icon|disk|cube|image|label|holder|tips|badge',
                button: 'icon|arrow|disk|cube|image|label|tips|badge',
                list: 'indent|arrow|icon|disk|cube|image(label|badge|tips|custom)'
            }
        },
    },
    {
        attr: 'b4-expand',
        prop: 'b4Expand',
        value: null,
    },
    {
        attr: 'b4-collapse',
        prop: 'b4Collapse',
        value: null,
    },
    {
        attr: 'b4-add',
        prop: 'b4Add',
        value: null,
    },
    {
        attr: 'b4-edit',
        prop: 'b4Edit',
        value: null,
    },
    {
        attr: 'b4-remove',
        prop: 'b4Remove',
        value: null,
    },
    {
        attr: 'b4-graft',
        prop: 'b4Graft',
        value: null,
    },
    {
        attr: 'on-rendered',
        prop: 'onRendered',
        value: null,
    },
    {
        attr: 'on-insertitem',
        prop: 'onInsertItem',
        value: null,
    },
    {
        attr: 'on-insertitems',
        prop: 'onInsertItems',
        value: null,
    },
    {
        attr: 'on-trigger',
        prop: 'onTrigger',
        value: null
    },
    {
        attr: 'on-added',
        prop: 'onAdded',
        value: null
    },
    {
        attr: 'on-edited',
        prop: 'onEdited',
        value: null
    },
    {
        attr: 'on-removed',
        prop: 'onRemoved',
        value: null
    },
    {
        attr: 'on-disabled',
        prop: 'onDisabled',
        value: null
    },
    {
        attr: 'on-enabled',
        prop: 'onEnabled',
        value: null
    },
    {
        attr: 'on-disabledall',
        prop: 'onDisabledAll',
        value: null
    },
    {
        attr: 'on-enabledall',
        prop: 'onEnabledAll',
        value: null
    },
    {
        attr: 'on-readonly',
        prop: 'onReadonly',
        value: null
    },
    {
        attr: 'on-readonlyall',
        prop: 'onReadonlyAll',
        value: null
    },
    {
        attr: 'on-expand',
        prop: 'onExpand',
        value: null
    },
    {
        attr: 'on-expanded',
        prop: 'onExpanded',
        value: null
    },
    {
        attr: 'on-collapse',
        prop: 'onCollapse',
        value: null
    },
    {
        attr: 'on-collapsed',
        prop: 'onCollapsed',
        value: null
    },
    {
        attr: 'on-expandall',
        prop: 'onExpandAll',
        value: null
    },
    {
        attr: 'on-expandedAll',
        prop: 'onExpandedAll',
        value: null
    },
    {
        attr: 'on-collapsedall',
        prop: 'onCollapsedAll',
        value: null
    },
    {
        attr: 'on-selected',
        prop: 'onSelected',
        value: null
    },
    {
        attr: 'on-deselected',
        prop: 'onDeselected',
        value: null
    },
    {
        attr: 'on-selectedall',
        prop: 'onSelectedAll',
        value: null
    },
    {
        attr: 'on-checked',
        prop: 'onChecked',
        value: null
    },
    {
        attr: 'on-unchecked',
        prop: 'onUnchecked',
        value: null
    },
    {
        attr: 'on-checkedall',
        prop: 'onCheckedAll',
        value: null
    },
    {
        attr: 'on-filled',
        prop: 'onFilled',
        value: null
    },
    {
        attr: 'on-turned',
        prop: 'onTurned',
        value: null
    },
    {
        attr: 'on-got',
        prop: 'onGot',
        value: null
    },
    {
        attr: 'on-set',
        prop: 'onSet',
        value: null
    },
    {
        attr: 'on-cleared',
        prop: 'onCleared',
        value: null
    },
    {
        attr: 'on-grafted',
        prop: 'onGrafted',
        value: null
    },
    {
        attr: 'on-updatedcont',
        prop: 'onUpdatedCont',
        value: null
    },
    {
        attr: 'on-request',
        prop: 'onRequest',
        value: null
    },
    {
        attr: 'on-toofew',
        prop: 'onTooFew',
        value: null
    },
    {
        attr: 'on-toomany',
        prop: 'onTooMany',
        value: null
    },
    {
        attr: 'on-locked',
        prop: 'onLocked',
        value: null
    },
    {
        attr: 'on-unlocked',
        prop: 'onUnlocked',
        value: null
    },
    {
        attr: 'on-output',
        prop: 'onOutput',
        value: null
    },
    ...optBase
];

const optDrag$1 = [
    {
        attr: 'drops',
        prop: 'drops',
        value: '',
    },
    {
        attr: 'parent',
        prop: 'parent',
        value: null,
    },
    {
        attr: 'handle',
        prop: 'handle',
        value: '',
    },
    {
        attr: 'data',
        prop: 'data',
        value: {},
    },
    {
        attr: 'mode',
        prop: 'mode',
        value: 'cut',
    },
    {
        attr: 'purpose',
        prop: 'purpose',
        value: 'sort',
    },
    {
        attr: 'point',
        prop: 'point',
        value: {
            before: ['t/3'],
            after: ['b/3'],
        },
    },
    {
        attr: 'holder',
        prop: 'holder',
        value: {
            from: false,
            to: false,
        },
    },
    {
        attr: 'arrow',
        prop: 'arrow',
        value: {
            enable: false,
            icon: `${prefix}icon-right`,
            placement: 'left',
            selector: '',
        },
    },
    {
        attr: 'gesture',
        prop: 'gesture',
        value: {},
    },
    {
        attr: 'delay',
        prop: 'delay',
        value: 50,
    },
    {
        attr: 'duration',
        prop: 'duration',
        value: 200,
    },
    {
        attr: 'b4-drop',
        prop: 'b4Drop',
        value: null,
    },
    {
        attr: 'on-dropping',
        prop: 'onDropping',
        value: null,
    },
    {
        attr: 'on-dropped',
        prop: 'onDropped',
        value: null,
    },
    {
        attr: 'on-dragstart',
        prop: 'onDragStart',
        value: null,
    },
    {
        attr: 'on-dragmove',
        prop: 'onDragMove',
        value: null,
    },
    {
        attr: 'on-dragend',
        prop: 'onDragEnd',
        value: null,
    },
    {
        attr: 'on-dragcancel',
        prop: 'onDragCancel',
        value: null,
    },
    ...optBase
];

const optFlip = [
    {
        attr: 'parent',
        prop: 'parent',
        value: ''
    },
    {
        attr: 'children',
        prop: 'children',
        value: '',
    },
    {
        attr: 'duration',
        prop: 'duration',
        value: 0,
    },
    {
        attr: 'easing',
        prop: 'easing',
        value: 'ease-out',
    },
    {
        attr: 'prevent',
        prop: 'prevent',
        value: false,
    },
    {
        attr: 'b4-add',
        prop: 'b4Add',
        value: null,
    },
    {
        attr: 'b4-remove',
        prop: 'b4Remove',
        value: null,
    },
    {
        attr: 'b4-play',
        prop: 'b4Play',
        value: null,
    },
    {
        attr: 'b4-play',
        prop: 'b4Play',
        value: null,
    },
    {
        attr: 'b4-playall',
        prop: 'b4PlayAll',
        value: null,
    },
    {
        attr: 'on-played',
        prop: 'onPlayed',
        value: null,
    },
    {
        attr: 'on-playedall',
        prop: 'onPlayedAll',
        value: null,
    },
    ...optBase
];

class Flip extends ModBaseListen {
    options = {};
    parentEl;
    flipEls;
    flipData;
    childrenObs;
    canPlay;
    playedEls;
    srcNode;
    allPlayed;
    parentH;
    static hostType = 'none';
    static optMaps = optFlip;
    constructor(options = {}, initial = true) {
        super();
        super.ready({
            options,
            maps: Flip.optMaps,
        });
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch (err) {
            err ? console.error(err) : console.warn(config.warn.init);
            return this;
        }
        this.parentEl = getEl(this.options.parent);
        this.allPlayed = true;
        this.playedEls = [];
        this.canPlay = !this.options.prevent;
        this.updateFlipEls();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    async play(target) {
        if (this.destroyed || !target?.flip)
            return;
        if (this.options.b4Play) {
            let resp = await this.options.b4Play.call(this, target);
            resp && (target = resp);
        }
        let restDur = target.flip.anim ? (target.flip.anim.effect.getTiming().duration - target.flip.anim.currentTime) : 0;
        target.flip.anim?.cancel();
        let nowTranslate = this.getTranslate(target), nowRect = this.getRect(target), dist = {
            x: target.flip.now.x - nowRect.x,
            y: target.flip.now.y - nowRect.y,
        };
        let isSamePos = target.flip.last.x === nowRect.x && target.flip.last.y === nowRect.y;
        if (!dist.x && !dist.y)
            return new Promise(resolve => { resolve(null); });
        target.flip.last = nowRect;
        let anim = target.animate([
            {
                transform: `translate(${dist.x + nowTranslate.x}px,${dist.y + nowTranslate.y}px)`,
            },
            {
                transform: `translate(${nowTranslate.x}px,${nowTranslate.y}px)`,
            }
        ], {
            duration: isSamePos ? restDur : this.getDuration(dist),
            easing: this.options.easing,
            fill: 'forwards'
        });
        target.flip.anim = anim;
        await new Promise(resolve => requestAnimationFrame(resolve));
        return new Promise(resolve => {
            anim.onfinish = () => {
                anim.cancel();
                this.setFirstRect(target);
                this.playedEls.push(target);
                super.listen({ name: 'played', params: [target] });
                resolve(null);
            };
        });
    }
    getDuration(dist) {
        return Math.max(0, getAutoDur(getHypotenuse(dist.x, dist.y)) + this.options.duration);
    }
    getTranslate(target) {
        let m = new DOMMatrix(style(target).transform);
        return { x: m.m41, y: m.m42 };
    }
    getRect(target) {
        let tmp = target.getBoundingClientRect();
        return {
            x: tmp.left,
            y: tmp.top,
        };
    }
    setFirstRect(target) {
        !target.flip && (target.flip = {});
        target.flip.last = this.getRect(target);
        target.flip.now = { ...target.flip.last };
    }
    setNowRects() {
        for (let k of this.flipEls) {
            k.flip.now = this.getRect(k);
        }
    }
    updateFlipEls() {
        if (!this.options.children) {
            this.flipEls = this.parentEl ? [...this.parentEl.children] : [];
        }
        else {
            this.flipEls = getEls(this.options.children, this.parentEl);
        }
        for (let k of this.flipEls)
            this.setFirstRect(k);
    }
    async add(target, cb) {
        let els = getEls(target);
        if (!els.length)
            return this;
        if (this.options.b4Add) {
            let resp = await this.options.b4Add.call(this, els);
            resp && (els = resp);
        }
        for (let k of els) {
            this.flipEls.push(k);
            this.setFirstRect(k);
        }
        super.listen({ name: 'added', cb, params: [els] });
        return this;
    }
    async remove(target, cb) {
        let els = getEls(target);
        if (!els.length)
            return this;
        if (this.options.b4Remove) {
            let resp = await this.options.b4Remove.call(this, els);
            resp && (els = resp);
        }
        this.flipEls = this.flipEls.filter((k) => !els.includes(k));
        for (let el of els)
            Reflect.deleteProperty(el, 'flip');
        super.listen({ name: 'removed', cb, params: [els] });
        return this;
    }
    async playAll(cb) {
        if (this.flipEls.length < 2 || !this.canPlay || this.destroyed)
            return this;
        if (this.options.b4PlayAll) {
            let resp = await this.options.b4PlayAll.call(this, this.flipEls);
            resp && (this.flipEls = resp);
        }
        let promises = [...this.flipEls].map(k => this.play(k));
        this.allPlayed = false;
        await Promise.all(promises);
        this.allPlayed = true;
        super.listen({ name: 'playedAll', cb, params: [unique(this.playedEls)] });
        this.playedEls = [];
        return this;
    }
    destroy(cb) {
        if (this.destroyed)
            return this;
        for (let k of this.flipEls) {
            k.flip.anim.cancel();
            Reflect.deleteProperty(k, 'flip');
        }
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

class Drag extends ModBaseListen {
    targetEl;
    options = {};
    curX;
    curY;
    preX;
    preY;
    isActive;
    enterTimer;
    leaveTimer;
    eventToggle;
    holdEl;
    enterCompare;
    globalEvent;
    moveEvent;
    enterEvent;
    leaveEvent;
    enterHold;
    leaveHold;
    dragHolder;
    dropHolder;
    dropArrow;
    orgHolder;
    drops;
    lastDrop;
    lastPoint;
    dftParams;
    dropOver;
    dropEnd;
    targetTag;
    holderAttr;
    orgVal;
    nowVal;
    orgStyle;
    gestureIns;
    tmpEl;
    flipIns;
    static hostType = 'node';
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            maps: optDrag$1,
            host: elem,
            component: false,
            spread: ['arrow']
        });
        if (!this.targetEl)
            throw new Error('The target node for drag start is missing!');
        this.dragHolder = createEl('div', { class: `${prefix}holder-drag` }, this.options.lang.holderDrag);
        this.dropHolder = createEl('div', { class: `${prefix}holder-drop` }, this.options.lang.holderDrop);
        this.dropArrow = createEl('i', { class: `${prefix}drop-arrow ${this.options.arrow.icon}`, placement: this.options.arrow.placement, point: 'inside' });
        this.orgHolder = null;
        this.drops = [this.dropHolder];
        this.lastDrop = null;
        this.lastPoint = '';
        this.dftParams = { data: this.options.data, from: this.targetEl, holder: this.dropHolder, to: this.lastDrop, mode: this.options.mode, purpose: this.options.purpose, drops: this.drops, point: this.lastPoint };
        this.dropOver = debounce((evt, target) => {
            let drop = this.drops.find((k) => contains(target, k));
            if (drop) {
                if (drop === this.dropHolder) {
                    this.toggleDropHolderAttr('set', this.lastDrop);
                    this.toggleDropAttr();
                }
                else {
                    this.toggleDropHolderAttr('remove');
                    this.toggleDropAttr(drop, 'ing');
                    this.updateHolderSize(this.dropHolder, drop, this.holderAttr === 'width' ? 'height' : this.holderAttr === 'height' ? 'width' : '');
                }
                if (drop === this.dropHolder || drop === this.targetEl)
                    return;
                this.lastDrop = drop;
                let arrowRefer = getEl(this.options.arrow.selector, drop) || drop;
                this.options.arrow.enable && arrowRefer.appendChild(this.dropArrow);
                let points = getRectPoints(evt, drop), isBefore = points.find((k) => this.options.point.before.includes(k)), isAfter = points.find((k) => this.options.point.after.includes(k)), params = { ...this.dftParams, orgEvt: evt, to: drop, points };
                if (this.options.purpose === 'sort' || this.options.purpose === 'auto') {
                    if (isBefore) {
                        this.lastPoint = 'before';
                        this.options.holder.to && drop.insertAdjacentElement('beforebegin', this.dropHolder);
                    }
                    else if (isAfter) {
                        this.lastPoint = 'after';
                        this.options.holder.to && drop.insertAdjacentElement('afterend', this.dropHolder);
                    }
                    else {
                        if (this.options.purpose === 'auto') {
                            this.lastPoint = 'inside';
                            elState(this.dropHolder).isCalc && this.dropHolder.remove();
                        }
                    }
                }
                else {
                    this.lastPoint = 'inside';
                }
                drop.setAttribute('point', this.lastPoint);
                this.options.arrow.enable && this.dropArrow.setAttribute('point', this.lastPoint);
                super.listen({ name: 'dropping', params: [{ ...params, point: this.lastPoint }] });
            }
            else {
                this.toggleDropAttr();
                this.toggleDropHolderAttr('remove');
                elState(this.dropHolder).isCalc && this.dropHolder.remove();
            }
        }, this.options.delay);
        this.dropEnd = async (evt, target) => {
            let drop = this.drops.find((k) => contains(target, k)), params = { ...this.dftParams, orgEvt: evt, to: this.lastDrop, point: this.lastPoint };
            this.options.arrow.enable && this.dropArrow.remove();
            if (drop) {
                if (this.options.b4Drop) {
                    let tmp = await this.options.b4Drop.call(this, params);
                    if (tmp === false) {
                        this.goback(this.options.mode, 'fail');
                        return true;
                    }
                }
                this.clearTransform();
                this.toggleDropAttr(drop, 'ed');
                drop.removeAttribute('point');
                super.listen({ name: 'dropped', params: [params] });
                return false;
            }
            else {
                this.goback(this.options.mode, 'fail');
                return true;
            }
        };
        this.targetTag = 'dragnode';
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.setAttrs();
        this.holderAttr = ['left', 'right'].includes(this.options.arrow.placement) ? 'height' :
            ['top', 'bottom'].includes(this.options.arrow.placement) ? 'width' : '';
        this.orgVal = transformTools.get(this.targetEl, ['translate'])['translate'];
        this.nowVal = { ...this.orgVal };
        this.orgStyle = this.targetEl.style.cssText;
        this.flipIns = new Flip({ children: this.options.drops, duration: 5000 }),
            this.getGestureIns();
        super.listen({ name: 'initiated', cb });
    }
    setAttrs() {
        this.targetEl.setAttribute([this.targetTag], '');
    }
    getGestureIns() {
        this.gestureIns = new Gesture(this.targetEl, {
            rotate: false,
            scale: false,
            drift: false,
            wheel: false,
            translate: {
                target: this.options.handle,
            },
            onTranslate: (data) => {
                if (data.target?.flip?.anim?.playState === 'running')
                    return;
                this.tmpEl = createEl('div', { class: `${prefix}drag-tmp` }, '正在拖拽');
                document.body.appendChild(this.tmpEl);
                this.orgHolder = this.targetEl.cloneNode(true);
                this.orgHolder.classList.add(`${prefix}holder-origin`);
                this.targetEl.setAttribute('drag', this.options.mode);
                document.body.style.cursor = 'grabbing';
                this.getDrops();
                if (this.options.mode === 'cut' && this.options.holder.from) {
                    this.updateHolderSize(this.dragHolder, this.targetEl);
                    this.targetEl.insertAdjacentElement('afterend', this.dragHolder);
                }
                if (this.options.mode === 'copy' && this.options.holder.from) {
                    this.targetEl.insertAdjacentElement('afterend', this.orgHolder);
                }
                super.listen({ name: 'dragStart', params: [{ ...data }] });
            },
            onTranslating: (data) => {
                if (data.target?.flip?.anim?.playState !== 'running')
                    return;
                this.tmpEl.style.left = `${data.coord.x + 8}px`;
                this.tmpEl.style.top = `${data.coord.y + 8}px`;
                this.getDrops();
                let dropEl = this.drops.find((k) => k.contains(data.evtTarget));
                if (dropEl && dropEl !== data.target && dropEl?.flip?.anim?.playState !== 'running') {
                    let dropParent = dropEl.parentNode, children = [...dropParent.children], sourceIdx = children.indexOf(data.target), targetIdx = children.indexOf(dropEl);
                    this.flipIns.setNowRects();
                    if (sourceIdx < targetIdx) {
                        dropParent.insertBefore(data.target, dropEl.nextElementSibling);
                    }
                    else {
                        dropParent.insertBefore(data.target, dropEl);
                    }
                    this.flipIns.playAll();
                }
                this.dropOver(data.orgEvt, data.evtTarget);
                
                super.listen({ name: 'dragMove', params: [{ ...data, }] });
            },
            onTranslated: (data) => {
                let k = setInterval(() => {
                    (data.target?.flip?.playing);
                    if (!data.target?.flip?.playing) {
                        clearInterval(k);
                    }
                }, 100);
                this.tmpEl.remove();
                
                super.listen({ name: 'dragEnd', params: [{ ...data }] });
            },
            onCanceled: (data) => {
                this.tmpEl.remove();
                super.listen({ name: 'dragCancel', params: [{ ...data }] });
            }
        });
    }
    updateHolderSize(target, relate, attr) {
        if (!relate)
            return;
        let styleRelate = style(relate), styleTarget = style(target), width = styleRelate.width, height = styleRelate.height;
        if (attr === 'width') {
            target.style.width = width;
            target.style.height = styleTarget.height;
        }
        else if (attr === 'height') {
            target.style.height = height;
            target.style.width = styleTarget.width;
        }
        else {
            target.style.width = width;
            target.style.height = height;
        }
    }
    toggleDropAttr(exclude, type) {
        if (exclude === this.dropHolder)
            return;
        exclude && exclude !== this.dropHolder && exclude.setAttribute('drop', type);
        for (let k of this.drops) {
            if (exclude === k)
                continue;
            k.removeAttribute('drop');
        }
    }
    toggleDropHolderAttr(type = 'set', relate) {
        if (type === 'remove') {
            this.dropHolder.removeAttribute('moving');
        }
        else {
            this.dropHolder.setAttribute('moving', '');
            relate && this.updateHolderSize(this.dropHolder, relate, this.holderAttr);
        }
    }
    clearTransform() {
        this.targetEl.style.cssText = this.targetEl.style.cssText.replace('transform', '');
    }
    getDrops(drops = this.options.drops) {
        this.drops = [...document.body.querySelectorAll(this.options.drops)];
    }
    updateDrops(val) {
        if (this.destroyed || isEmpty(val))
            return;
        this.options.drops = val;
        return this;
    }
    goback(type, state) {
        if (state === 'succ') {
            if (type === 'copy') {
                this.targetEl.style.transitionDuration = `0ms`;
                transformTools.set({
                    el: this.targetEl,
                    data: {
                        translate: this.orgVal,
                    }
                });
                this.toggleDropAttr();
                this.restore();
            }
        }
        else if (state === 'fail') {
            ease({
                from: this.nowVal,
                to: this.orgVal,
                doing: (data) => {
                    transformTools.set({
                        el: this.targetEl,
                        data: {
                            translate: data.value,
                        }
                    });
                },
                done: () => {
                    this.restore();
                },
                duration: this.options.duration,
            });
            this.toggleDropAttr();
        }
    }
    restore() {
        this.targetEl.removeAttribute('drag');
        this.dropHolder.removeAttribute('moving');
        this.dropHolder.style.cssText = this.dropHolder.style.cssText.replace('width', '').replace('height', '');
        elState(this.dragHolder).isCalc && this.dragHolder.remove();
        elState(this.dropHolder).isCalc && this.dropHolder.remove();
        elState(this.orgHolder).isCalc && this.orgHolder.remove();
        elState(this.dropArrow).isCalc && this.dropArrow.remove();
        this.lastDrop = null;
        this.lastPoint = '';
        document.body.style.cssText = document.body.style.cssText.replace('cursor', '');
    }
    destroy(cb) {
        if (this.destroyed)
            return;
        this.restore();
        this.toggleDropAttr();
        this.gestureIns.destroy();
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

let AXTMP_emptyTpl = `<i class="${prefix}c-ignore">${config.lang.tags.emptyholder}</i>`, AXTMP_editorHolder = config.lang.tags.placeholder, AXTMP_separator$3 = config.splitHyphen;
const optTags = [
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {}
    },
    {
        attr: 'content',
        prop: 'content',
        value: ''
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'theme',
        prop: 'theme',
        value: '',
    },
    {
        attr: 'size',
        prop: 'size',
        value: 'md',
    },
    {
        attr: 'field',
        prop: 'field',
        value: 'label',
    },
    {
        attr: 'type',
        prop: 'type',
        value: '',
    },
    {
        attr: 'compact',
        prop: 'compact',
        value: false,
    },
    {
        attr: 'shape',
        prop: 'shape',
        value: '',
    },
    {
        attr: 'max',
        prop: 'max',
        value: 0,
    },
    {
        attr: 'min',
        prop: 'min',
        value: 0,
    },
    {
        attr: 'sliced',
        prop: 'sliced',
        value: true,
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'unique',
        prop: 'unique',
        value: {
            enable: false,
            refer: 'id',
            template: `{{this.field}}<i class="${prefix}c-brief">({{this.key}}:{{this.value}})</i>`,
        },
    },
    {
        attr: 'empty',
        prop: 'empty',
        value: {
            enable: true,
            content: AXTMP_emptyTpl,
        }
    },
    {
        attr: 'editor',
        prop: 'editor',
        value: {
            enable: false,
            addable: true,
            deletable: true,
            selector: '',
            placeholder: AXTMP_editorHolder,
        },
    },
    {
        attr: 'removable',
        prop: 'removable',
        value: false,
    },
    {
        attr: 'separator',
        prop: 'separator',
        value: AXTMP_separator$3,
    },
    {
        attr: 'b4-fill',
        prop: 'b4Fill',
        value: null,
    },
    {
        attr: 'b4-add',
        prop: 'b4Add',
        value: null,
    },
    {
        attr: 'b4-remove',
        prop: 'b4Remove',
        value: null,
    },
    {
        attr: 'b4-edit',
        prop: 'b4Edit',
        value: null,
    },
    {
        attr: 'b4-clear',
        prop: 'b4Clear',
        value: null,
    },
    {
        attr: 'on-added',
        prop: 'onAdded',
        value: null,
    },
    {
        attr: 'on-removed',
        prop: 'onRemoved',
        value: null,
    },
    {
        attr: 'on-edited',
        prop: 'onEdited',
        value: null,
    },
    {
        attr: 'on-cleared',
        prop: 'onCleared',
        value: null,
    },
    {
        attr: 'on-duplicated',
        prop: 'onDuplicated',
        value: null,
    },
    {
        attr: 'on-request',
        prop: 'onRequest',
        value: null
    },
    {
        attr: 'on-output',
        prop: 'onOutput',
        value: null
    },
    ...optBase
];

class Tags extends ModBaseListenCache {
    options = {};
    emptyEl;
    content;
    editEvent;
    toggleSelected;
    output;
    maxIndex;
    dataOrig;
    dataObs;
    data;
    editEl;
    last;
    labelEl;
    static hostType = 'node';
    static optMaps = optTags;
    constructor(elem, options = {}, initial = config.initial) {
        super();
        super.ready({
            options,
            maps: Tags.optMaps,
            host: elem,
            component: true,
            spread: ['edit', 'unique', 'empty']
        });
        
        
        
        
        
        
        
        
        
        
        
        
        this.emptyEl = createEl('span', { [alias]: 'empty' }, this.options.empty.content);
        
        this.content = '';
        let _this = this;
        this.editEvent = async (e) => {
            let label = this.editEl.value.trim(), selectedItem = this.data.find((k) => k.selected);
            if (label) {
                this.editEl.style.width = label.length + 'em';
                if (this.options.editor.addable && e.code === 'Enter') {
                    this.add(label, () => {
                        this.editEl.value = '';
                        this.editEl.focus();
                    });
                }
                selectedItem && (selectedItem.selected = false);
            }
            else {
                this.editEl.style.cssText = this.editEl.style.cssText.replace('width:', '');
                if (this.options.editor.deletable && e.code === 'Backspace' && this.data.length > 0) {
                    let endItem = this.data.slice(-1)[0];
                    if (endItem.selected) {
                        this.remove(endItem);
                    }
                    else {
                        endItem.selected = true;
                    }
                }
                else {
                    selectedItem && (selectedItem.selected = false);
                }
            }
        };
        this.toggleSelected = function (e) {
            if (e.target.getAttribute(alias) !== 'remove') {
                let item = _this.data.find((k) => k.wrapEl === this);
                item.selected = true;
                _this.data.filter((k) => k !== item).forEach((k) => {
                    k.selected = false;
                });
            }
        };
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch (err) {
            err ? console.error(err) : console.warn(config.warn.init);
            return this;
        }
        this.output = { value: '', raw: '', items: [] };
        this.maxIndex = 0;
        this.initContent();
        this.setEmpty();
        this.createEditor();
        await getContent.call(this, {
            content: this.content,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: {
                xhrName: 'contXhr',
                spinSel: this.targetEl,
                ...this.options.ajax
            },
            cb: async (data) => {
                await this.renderContent(data);
            }
        });
        this.setAttrs();
        this.renderFinish();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    initContent() {
        if (this.options.content) {
            this.content = this.options.content;
        }
        else {
            this.content = this.rawHtml || this.targetEl.innerHTML;
        }
    }
    setDataObs() {
        this.dataOrig = [];
        this.dataObs = new Observe(this.dataOrig, {
            deep: true,
            onSet: (obj) => {
                if (obj.key === this.options.field || obj.key === 'label') {
                    obj.target.labelEl.innerHTML = obj.value;
                }
                else if (['selected', 'disabled'].includes(obj.key)) {
                    obj.target.wrapEl.toggleAttribute(obj.key, obj.value);
                }
                else if (obj.key === 'theme') {
                    obj.target.wrapEl.setAttribute(obj.key, obj.value);
                }
                else if (obj.key === 'icon' && obj.proxy.iconEl) {
                    classes(obj.proxy.iconEl).replace(obj.raw, obj.value);
                }
                else if (obj.key === 'cube' && obj.proxy.cubeEl) {
                    obj.proxy.cubeEl.src = obj.value;
                }
                else if (obj.key === 'disk' && obj.proxy.diskEl) {
                    obj.proxy.diskEl.src = obj.value;
                }
                else if (obj.key === 'image' && obj.proxy.imageEl) {
                    obj.proxy.imageEl.src = obj.value;
                }
                else if (['href', 'target', 'rel', 'download'].includes(obj.key) && this.labelEl.nodeName === 'A') {
                    obj.target.labelEl[obj.key] = obj.value;
                }
            },
            onCompleted: (data) => {
                this.toggleEmpty();
                if (this.options.storName) {
                    let tmp = {
                        content: !isEmpty(this.options.content) ? deepClone(this.dataOrig) : '',
                    };
                    super.updateCache(tmp);
                }
                this.output.value = this.getStrVals();
                this.output.items = [...this.data];
                super.listen({ name: 'output', params: [this.output] });
            }
        });
        this.data = this.dataObs.proxy;
    }
    async renderContent(data) {
        try {
            if (this.options.b4Fill) {
                let resp = await this.options.b4Fill.call(this, data, this.targetEl);
                resp && (data = resp);
            }
        }
        catch (err) {
            err ? console.error(err) : console.warn('The content was blocked from being filled!');
            return this;
        }
        this.setDataObs();
        let tmp = this.getSource(data);
        try {
            let resp = await this.moreExceed({ data: tmp, source: this.data });
            resp && (tmp = resp);
        }
        catch {
            return;
        }
        this.render(tmp);
        this.data.push(...tmp);
    }
    getStrVals() {
        return this.data.map((k) => k[this.options.field]).join(this.options.separator);
    }
    getSource(obj) {
        let result = [];
        if (!isEmpty(obj)) {
            if (Array.isArray(obj)) {
                if (typeof obj[0] === 'string') {
                    result = obj.map(k => {
                        return { [this.options.field]: k, theme: '' };
                    });
                }
                else {
                    result = obj;
                }
            }
            else if (typeof obj === 'object') {
                result = [obj];
            }
            else if (typeof obj === 'string') {
                let tmp = createEl('div', {}, obj), children = [...tmp.children];
                if (children.length) {
                    if (children[0].nodeName === 'SELECT') {
                        result = select2Tree(children[0]);
                    }
                    else if (children[0].nodeName === 'INPUT') {
                        result = treeTools.fromInput(children[0], this.options.separator);
                    }
                    else {
                        for (let k of children) {
                            let obj = {};
                            k.hasAttribute('label') && (obj.label = k.getAttribute('label'));
                            k.hasAttribute('value') && (obj.value = k.getAttribute('value'));
                            k.hasAttribute('icon') && (obj.icon = k.getAttribute('icon'));
                            k.hasAttribute('disk') && (obj.disk = k.getAttribute('disk'));
                            k.hasAttribute('cube') && (obj.cube = k.getAttribute('cube'));
                            k.hasAttribute('image') && (obj.image = k.getAttribute('image'));
                            k.hasAttribute('disabled') && (obj.disabled = true);
                            k.hasAttribute('theme') && (obj.theme = k.getAttribute('theme'));
                            obj[this.options.field] = k.hasAttribute(this.options.field) ? k.getAttribute(this.options.field) : k.textContent;
                            if (k.nodeName === 'A') {
                                obj = {
                                    ...obj,
                                    href: k.getAttribute('href'),
                                    target: k.target,
                                    rel: k.rel,
                                    download: k.download,
                                };
                            }
                            result.push(obj);
                        }
                    }
                }
                else {
                    let arr = obj.trim().split(this.options.separator);
                    result = arr.map(k => {
                        return { [this.options.field]: k, theme: '' };
                    });
                }
            }
        }
        result = result.filter((k) => k && !this.data.includes(k));
        result.forEach((k, i) => {
            !k.hasOwnProperty('id') && (k.id = this.maxIndex + i);
        });
        this.maxIndex += result.length;
        return result;
    }
    render(content) {
        if (!isEmpty(content)) {
            let fragment = document.createDocumentFragment();
            for (let k of content) {
                k.wrapEl = this.createTag(k);
                fragment.appendChild(k.wrapEl);
            }
            this.targetEl.appendChild(fragment);
        }
        this.options.editor.enable && !this.options.editor.selector && this.targetEl.appendChild(this.editEl);
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}tags`);
        classes(this.targetEl).add(this.options.classes);
        this.options.theme ? this.targetEl.setAttribute('theme', this.options.theme) : this.targetEl.removeAttribute('theme');
        this.options.type ? this.targetEl.setAttribute('type', this.options.type) : this.targetEl.removeAttribute('type');
        this.targetEl.toggleAttribute('compact', this.options.compact);
        this.options.shape ? this.targetEl.setAttribute('shape', this.options.shape) : this.targetEl.removeAttribute('shape');
        this.options.size ? this.targetEl.setAttribute('size', this.options.size) : this.targetEl.removeAttribute('size');
    }
    setEmpty() {
        this.targetEl.innerHTML = '';
    }
    useLegend(obj) {
        let imgNone = getImgNone();
        if (obj.hasOwnProperty('icon')) {
            obj.iconEl = createEl('i', { [alias]: 'icon', class: `${obj.icon}` });
            obj.labelEl.insertAdjacentElement('beforebegin', obj.iconEl);
        }
        if (obj.hasOwnProperty('disk')) {
            obj.diskEl = createEl('img', { [alias]: 'disk', src: `${obj.disk || imgNone}` });
            obj.labelEl.insertAdjacentElement('beforebegin', obj.diskEl);
        }
        if (obj.hasOwnProperty('cube')) {
            obj.cubeEl = createEl('img', { [alias]: 'cube', src: `${obj.cube || imgNone}` });
            obj.labelEl.insertAdjacentElement('beforebegin', obj.cubeEl);
        }
        if (obj.hasOwnProperty('image')) {
            obj.imageEl = createEl('img', { [alias]: 'image', src: `${obj.image || imgNone}` });
            obj.labelEl.insertAdjacentElement('beforebegin', obj.imageEl);
        }
    }
    createTag(obj) {
        let labelObj = { [alias]: 'label' }, text = this.data.find((k) => k !== obj && k[this.options.field] === obj[this.options.field]) ?
            renderTpl(this.options.unique.template, { field: obj[this.options.field], value: obj[this.options.unique.refer], key: this.options.unique.refer }) :
            obj[this.options.field];
        obj.hasOwnProperty('href') && (labelObj.href = obj.href);
        obj.hasOwnProperty('href') && obj.target && (labelObj.target = obj.target);
        obj.hasOwnProperty('href') && obj.rel && (labelObj.rel = obj.rel);
        obj.hasOwnProperty('href') && obj.download && (labelObj.download = obj.download);
        obj.labelEl = createEl(labelObj.hasOwnProperty('href') ? 'a' : 'i', labelObj, text);
        obj.wrapEl = createEl('span', { class: `${prefix}tag` }, obj.labelEl);
        obj.removeEl = createEl('i', { [alias]: 'remove' });
        this.options.removable && obj.wrapEl.appendChild(obj.removeEl);
        this.useLegend(obj);
        !this.options.theme && obj.theme && obj.wrapEl.setAttribute('theme', obj.theme);
        obj.disabled && obj.wrapEl.setAttribute('disabled', '');
        obj.selected && obj.wrapEl.setAttribute('selected', '');
        return obj.wrapEl;
    }
    createEditor() {
        if (!this.options.editor.enable || this.destroyed)
            return;
        let tmp = getEl(this.options.editor.selector);
        if (!tmp) {
            this.editEl = createEl('input', { type: 'text', placeholder: this.options.editor.placeholder });
        }
        else {
            this.editEl = tmp;
            !this.editEl.hasAttribute('placeholder') && this.editEl.setAttribute('placeholder', this.options.editor.placeholder);
        }
        this.editEl.removeEventListener('keyup', this.editEvent);
        this.editEl.addEventListener('keyup', this.editEvent);
    }
    toggleEmpty() {
        if (!this.options.empty.enable)
            return;
        if (!this.data.length) {
            this.targetEl.insertAdjacentElement('afterbegin', this.emptyEl);
            this.maxIndex = 0;
        }
        else {
            this.emptyEl.remove();
        }
    }
    renderFinish() {
        this.toggleEmpty();
        for (let k of this.data) {
            k.wrapEl.addEventListener('click', this.toggleSelected, false);
            k.removeEl.onclick = () => {
                this.remove(k);
            };
        }
        this.last = this.dataOrig[this.data.length - 1];
    }
    getVals(cb) {
        if (this.destroyed)
            return this;
        let value = {
            value: this.getStrVals(),
            items: [...this.data],
        };
        super.listen({ name: 'got', cb, params: [value] });
        return value;
    }
    
    async updateCont(content, cb) {
        if (this.destroyed)
            return this;
        this.setEmpty();
        await getContent.call(this, {
            content,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: {
                xhrName: 'contXhr',
                spinSel: this.targetEl,
                ...this.options.ajax
            },
            cb: async (data) => {
                this.saveRaw();
                await this.renderContent(data);
                this.renderFinish();
                super.updateCache({ content });
                super.listen({ name: 'updatedCont', cb, params: [this.dataOrig] });
            }
        });
        return this;
    }
    saveRaw() {
        this.output.raw = this.getStrVals();
    }
    async add(data, cb) {
        if (isEmpty(data) || this.destroyed)
            return this;
        let arr = this.getSource(data), dupTags = [], oldLen = 0, oldValues = this.data.map((k) => k[this.options.field]);
        if (this.options.unique.enable) {
            arr = unique(arr, this.options.field);
            oldLen = arr.length;
            arr = arr.map((k) => {
                let output;
                if (oldValues.includes(k[this.options.field])) {
                    dupTags.push(k);
                }
                else {
                    output = k;
                }
                return output;
            }).filter(Boolean);
            if (oldLen !== arr.length) {
                new Message({
                    content: arr.length === 0 ? this.options.lang.includeFull : this.options.lang.includePart,
                    status: 'warn',
                    iconShow: true,
                }).show();
                super.listen({ name: 'duplicated', params: [dupTags] });
            }
        }
        if (arr.length === 0)
            return this;
        try {
            let resp = await super.moreExceed({ data: arr, source: this.data });
            resp && (arr = resp);
        }
        catch {
            return;
        }
        try {
            if (this.options.b4Add) {
                let resp = await this.options.b4Add.call(this, arr);
                resp && (arr = resp);
            }
        }
        catch (err) {
            err ? console.error(err) : console.warn('Failed to add a new tags!');
            return this;
        }
        if (!Array.isArray(arr) || !arr.length)
            return this;
        this.saveRaw();
        this.render(arr);
        this.data.push(...arr);
        this.renderFinish();
        super.listen({ name: 'added', cb, params: [arr] });
        return this;
    }
    async remove(data, cb) {
        if (isEmpty(data) || this.destroyed || !this.data.length)
            return this;
        let tags = findItems(data, this.data, '', { node: 'wrapEl', string: this.options.field, separator: this.options.separator });
        try {
            if (this.options.b4Remove) {
                let resp = await this.options.b4Remove.call(this, tags);
                resp && (tags = resp);
            }
        }
        catch (err) {
            err ? console.error(err) : console.warn('Failed to delete the old tags!');
            return this;
        }
        try {
            let resp = await this.lessExceed({ data: tags, source: this.data });
            resp && (tags = resp);
        }
        catch {
            return;
        }
        if (!Array.isArray(tags) || !tags.length)
            return this;
        this.saveRaw();
        for (let i = 0; i < this.data.length; i++) {
            let tmp = this.data[i];
            if (tags.includes(tmp)) {
                this.data.splice(i--, 1);
                tmp.wrapEl.remove();
            }
        }
        super.listen({ name: 'removed', cb, params: [tags] });
        return this;
    }
    async edit(item, data, cb) {
        if (isEmpty(item) || this.destroyed || !this.data.length)
            return this;
        let tag = findItem(item, this.data, '', { node: 'wrapEl', string: this.options.field });
        if (!tag) {
            return this;
        }
        try {
            if (this.options.b4Edit) {
                let resp = await this.options.b4Edit.call(this, tag);
                resp && (tag = resp);
            }
        }
        catch (err) {
            err ? console.error(err) : console.warn('Failed to eidit the tag!');
            return this;
        }
        if (!tag)
            return this;
        Object.assign(tag, data);
        super.listen({ name: 'edited', cb, params: [tag] });
        return this;
    }
    async clear(cb) {
        if (this.destroyed || !this.data.length)
            return this;
        this.options.b4Clear && await this.options.b4Clear.call(this);
        this.saveRaw();
        this.data.splice(0);
        this.maxIndex = 0;
        this.targetEl.innerHTML = '';
        this.options.editor.enable && this.targetEl.appendChild(this.editEl);
        super.listen({ name: 'cleared', cb });
        return this;
    }
    
    destroy(cb) {
        this.data.forEach((k) => {
            k.wrapEl.removeEventListener('click', this.toggleSelected);
            k.removeEl.onclick = null;
        });
        this.options.editor.enable && this.editEl.removeEventListener('keyup', this.editEvent);
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

class Tree extends ModBaseListenCacheNest {
    options = {};
    treeDataOrig;
    searchs;
    ignores;
    editorEl;
    lastExpanded;
    dropTag;
    pagination;
    searchEl;
    resultIns;
    resultEl;
    inputEl;
    output;
    value;
    expandEvt;
    selectEvt;
    searchEvgt;
    lineEvt;
    bubbleIns;
    receiver;
    hoverIns;
    contXhr;
    floorMax;
    chainChecking;
    seqItems;
    maxIndex;
    excludeDrags;
    rawData;
    observeIns;
    static hostType = 'node';
    static optMaps = optTree;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            host: elem,
            maps: Tree.optMaps,
            component: true,
            spread: ['output', 'arrow', 'check', 'legend', 'select', 'shortcut', 'tools', 'drag', 'paginated', 'bubble']
        });
        
        
        
        
        
        
        
        
        
        
        
        
        
        let _this = this;
        this.expandEvt = debounce(function () {
            let attr = this.getAttribute(alias) === 'arrow' ? 'arrowEl' : this.getAttribute(alias) === 'legend' ? 'legendEl' : 'headEl', item = findItem(this, _this.flatData, attr);
            _this.toggleExpanded(item);
        }, this.options.delay);
        this.selectEvt = debounce(function () {
            let item = findItem(this, _this.flatData, 'labelEl');
            _this.toggleSelected(item);
            if (item && item.toolsEl && _this.options.tools.trigger === 'click') {
                if (item.selected) {
                    show({ el: item.toolsEl });
                }
                else {
                    hide({ el: item.toolsEl });
                }
            }
        }, this.options.delay);
        this.searchEvgt = debounce(function () {
            _this.search(this.value);
        }, this.options.search.delay);
        this.lineEvt = debounce(function (e) {
            if (!_this.options.shortcut.enable)
                return;
            let item = findItem(this, _this.flatData, 'headEl'), condition = false;
            if (!item)
                return;
            if (_this.options.shortcut.span === 'blank' && (e.target === item.headEl || ['holder', 'group', 'gap'].includes(e.target.getAttribute(alias)))) {
                condition = true;
            }
            else if (!contains(e.target, [item.arrowEl, item.checkEl, item.toolsEl, ((_this.options.select.enable && !item.headEl.hasAttribute('unselectable')) || (item.labelEl.nodeName === 'A' && item.labelEl.hasAttribute('href'))) ? item.labelEl : null])) {
                condition = true;
            }
            if (!condition)
                return;
            if (_this.options.shortcut.mean === 'selected') {
                _this.toggleSelected(item);
            }
            else if (_this.options.shortcut.mean === 'checked') {
                !item.checkEl.hasAttribute('disabled') && _this.toggleCheck(item);
            }
            else if (_this.options.shortcut.mean === 'expanded') {
                _this.toggleExpanded(item);
            }
            else if (_this.options.shortcut.mean === 'auto') {
                if (item.children) {
                    _this.toggleExpanded(item);
                }
                else {
                    _this.options.check.enable && !item.checkEl.hasAttribute('disabled') ? _this.toggleCheck(item) : _this.toggleSelected(item);
                }
            }
        }, this.options.delay);
        this.editorEl = createEl('input', { type: 'text' });
        this.lastExpanded = -1;
        this.dropTag = this.options.drop.attr || 'dropnode';
        this.treeDataOrig = [];
        this.pagination = { label: this.options.lang.paginated.main, id: this.options.rootStart, children: [], wrapEl: this.targetEl };
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch (err) {
            err ? console.error(err) : console.warn(config.warn.init);
            return this;
        }
        this.output = { value: '', raw: '', items: [] };
        this.correctOpts();
        super.useTpl();
        this.searchs = [];
        this.floorMax = 0;
        this.chainChecking = false;
        this.seqItems = [];
        this.setFeature();
        this.maxIndex = this.options.idStart;
        if (this.options.bubble.enable) {
            let host = this.targetEl;
            this.receiver = getEl(this.options.output.target) || this.targetEl;
            (this.receiver.nodeName.includes('INPUT') || this.receiver.nodeName.includes('TEXTAREA')) && this.receiver.toggleAttribute('readonly', true);
            this.targetEl = createEl('div');
            let footParam = this.options.output.enable && !this.options.output.instant ? {
                enable: true,
                children: [
                    'close',
                    {
                        name: 'clear',
                        action: (resp) => {
                            resp.el.onclick = () => {
                                this.clearVals();
                            };
                        },
                    },
                    {
                        name: 'confirm',
                        action: (resp) => {
                            resp.el.onclick = () => {
                                this.setVals();
                                this.bubbleIns.hide();
                            };
                        }
                    }
                ]
            } : false;
            this.bubbleIns = new Function('host', 'module', 'options', `"use strict";return new module(host,options)`)(host, ax[capStart(this.options.bubble.type.trim())], Object.assign({
                trigger: 'click',
                content: this.targetEl,
                contType: 'node',
                placement: this.options.bubble.type === 'popup' ? 'bottom-start' : '',
                footer: footParam,
            }, this.options.bubble));
        }
        else {
            this.receiver = getEl(this.options.output.target);
        }
        await this.getDataToRender();
        this.excludeDrags = !isEmpty(this.options.drag.exclude) ? findItems(this.options.drag.exclude, this.flatData) : [];
        this.setAttrs();
        this.setResult();
        this.renderFinish();
        super.initCheckeds();
        this.initExpandeds();
        this.initSelecteds();
        super.initDisableds();
        super.initReadonlys();
        this.updateResult(this.getVals().value);
        this.setSearch();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    correctOpts() {
        if (!isEmpty(this.options.value)) {
            if (this.options.output.from === 'checked') {
                this.options.check.value = this.options.value;
            }
            else {
                this.options.select.value = this.options.value;
            }
        }
    }
    setFeature() {
        if (!this.options.feature.type)
            return;
        extend({
            target: this.options,
            source: {
                shortcut: {
                    enable: true,
                    mean: 'checked',
                    span: 'whole'
                },
                expand: { ...this.options.feature.expand },
                check: {
                    enable: true,
                    ...this.options.feature.check
                },
                select: {
                    enable: false,
                },
                arrow: {
                    enable: !this.options.feature.expand.all || this.options.feature.expand.only ? true : false
                },
                output: {
                    from: 'checked'
                },
                layout: this.options.feature.layout[this.options.feature.type]
            }
        });
    }
    async sqlToAdd(item) {
        if (!this.options.content)
            return;
        let promise = treeTools.allToTree({
            content: this.options.content,
            contType: this.options.contType,
            contData: { pId: item.id, pLabel: item.label, pValue: item.value, ...this.options.sqlData },
            ajax: { spinSel: item.arrowEl, xhrName: 'contXhr' },
            ins: this,
            fill: false,
        });
        await promise.then(async (resp) => {
            let data = resp.hasOwnProperty('data') && Array.isArray(resp.data) ? resp.data : resp;
            await this.add({
                data,
                target: item,
                isFront: false,
                autoFill: !this.options.paginated.enable,
            });
            this.options.paginated.enable && this.turnFirstPage(item);
        });
    }
    async getDataToRender() {
        let base = {
            rootStart: this.options.rootStart,
            idStart: this.options.idStart,
            floorStart: this.options.floorStart,
        }, promise;
        if (this.options.content) {
            promise = treeTools.allToTree({
                content: this.options.content,
                contType: this.options.contType,
                contData: this.options.contData,
                ajax: { spinSel: this.targetEl, xhrName: 'contXhr' },
                ins: this,
                ...base
            });
        }
        else {
            promise = treeTools.allToTree({
                content: this.targetEl.innerHTML,
                ...base
            });
        }
        await promise.then((res) => {
            
            this.treeDataOrig = res.data && Array.isArray(res.data) ? res.data : res;
            this.rawData = res;
        });
        this.targetEl.innerHTML = '';
        await this.renderData(this.treeDataOrig);
        this.getTreeFlat();
        super.listen({ name: 'rendered', params: [this.treeData, this.flatData] });
        return this;
    }
    getTreeFlat() {
        this.treeData = this.getObserver(this.treeDataOrig).proxy;
        this.pagination.children = this.treeData;
        
        this.flatData = treeTools.toFlat(this.treeData);
    }
    getObserver(data) {
        this.observeIns = new Observe(data, {
            deep: {
                enable: true,
                exclude: ['tools'],
            },
            onSet: (obj) => {
                if (obj.key === 'icon' && obj.proxy.iconEl) {
                    classes(obj.proxy.iconEl).replace(obj.raw, obj.value);
                }
                else if (obj.key === 'cube' && obj.proxy.cubeEl) {
                    obj.proxy.cubeEl.src = obj.value;
                }
                else if (obj.key === 'disk' && obj.proxy.diskEl) {
                    obj.proxy.diskEl.src = obj.value;
                }
                else if (obj.key === 'image' && obj.proxy.imageEl) {
                    obj.proxy.imageEl.src = obj.value;
                }
                else if (obj.key === 'label') {
                    obj.proxy.labelEl.innerHTML = obj.value;
                }
                else if (obj.key === 'tips' && obj.proxy.tipsEl) {
                    obj.proxy.tipsEl.innerHTML = obj.value;
                }
                else if (obj.key === 'brief' && obj.proxy.briefEl) {
                    super.updateElCont(obj.proxy, obj.value);
                }
                else if (obj.key === 'custom' && obj.proxy.customEl) {
                    super.updateElCont(obj.proxy, obj.value, 'custom');
                }
                else if (obj.key === 'badge' && obj.proxy.badgeEl) {
                    obj.proxy.badgeEl.setAttribute('label', obj.value);
                }
                else if (obj.key === 'href' && obj.proxy.labelEl.nodeName === 'A') {
                    obj.proxy.labelEl.href = obj.value;
                }
                else if (obj.key === 'rel' && obj.proxy.labelEl.nodeName === 'A') {
                    obj.proxy.labelEl.rel = obj.value;
                }
                else if (obj.key === 'target' && obj.proxy.labelEl.nodeName === 'A') {
                    obj.proxy.labelEl.target = obj.value;
                }
                else if (obj.key === 'floor') {
                    obj.proxy.indentHeadEl.innerHTML = obj.proxy.indentBodyEl.innerHTML = obj.proxy.indentFootEl.innerHTML = this.getIndentHtml(obj.value);
                    this.updateChildrenFloor(obj.proxy);
                }
                else if (obj.key === 'atEnd') ;
                else if (obj.key === 'expanded' && obj.proxy.children) {
                    obj.proxy.arrowEl && super.toggleArrow(obj.value, obj.proxy);
                    this.toggleParentLegend(obj.proxy);
                }
                else if (obj.key === 'selected') {
                    obj.proxy.headEl.toggleAttribute('selected', obj.value);
                    this.updateSeqItems(obj, 'selected');
                }
                else if (obj.key === 'disabled') {
                    obj.proxy.headEl.toggleAttribute('disabled', obj.value);
                }
                else if (obj.key === 'readonly') {
                    obj.proxy.headEl.toggleAttribute('readonly', obj.value);
                }
                else if (obj.key === 'checked') {
                    obj.proxy.headEl.toggleAttribute('checked', obj.value);
                    obj.proxy.checkEl && obj.proxy.checkEl.setAttribute('check', obj.value ? 'ed' : '');
                    this.updateSeqItems(obj, 'checked');
                }
                else if (obj.key === 'children') {
                    if (!obj.raw && obj.value) {
                        this.child2Parent(obj.proxy);
                    }
                    else if (obj.raw && !obj.value) {
                        this.parent2Child(obj.proxy);
                    }
                }
            },
            onDeleted: (obj) => {
                if (obj.key === 'children') {
                    treeTools.parentToChild(obj.proxy);
                }
            },
            onCompleted: (data) => {
                ((data.keys.set.includes('selected') && this.options.output.from === 'selected')
                    ||
                        (data.keys.set.includes('checked') && this.options.output.from === 'checked'))
                    && this.updateVals();
                if (this.options.check.enable && this.options.check.max) {
                    let checkeds = super.getCheckeds(), uncheckeds = super.getUncheckeds(), setDisabled = (data) => {
                        for (let k of data) {
                            !k.disabled && k.checkEl.toggleAttribute('disabled', true);
                            k.headEl.toggleAttribute('exceeded', true);
                        }
                    };
                    if (checkeds.length >= this.options.check.max) {
                        setDisabled(uncheckeds);
                        super.listen({ name: 'stopChecked', params: [uncheckeds] });
                    }
                    else if (checkeds.length <= this.options.check.min) {
                        setDisabled(checkeds);
                        super.listen({ name: 'stopUnchecked', params: [checkeds] });
                    }
                    else {
                        for (let k of this.flatData) {
                            k.checkEl.removeAttribute('disabled');
                            k.headEl.toggleAttribute('exceeded', false);
                        }
                        super.listen({ name: 'rechecked' });
                    }
                }
                if (this.options.storName) {
                    let tmp = {
                        select: { value: treeTools.getBoolItems(this.flatData, 'selected').map((k) => k.id) },
                        check: { value: treeTools.getBoolItems(this.flatData, 'checked').map((k) => k.id) },
                        expand: treeTools.getBoolItems(this.flatData, 'expanded').map((k) => k.id),
                        disable: treeTools.getBoolItems(this.flatData, 'disabled').map((k) => k.id),
                        readonly: treeTools.getBoolItems(this.flatData, 'readonly').map((k) => k.id),
                        content: !isEmpty(this.options.content) ? deepClone(this.treeDataOrig) : '',
                    };
                    super.updateCache(tmp);
                }
            }
        });
        return this.observeIns;
    }
    updateSeqItems(data, type = 'selected') {
        this.options.output.from === type && splice({ host: this.seqItems, source: data.proxy, intent: data.value ? 'end+' : 'remove' });
        if (this.seqItems.length) {
            if ((type === 'checked' && this.options.check.type === 'radio') || (type === 'selected' && this.options.select.only)) {
                this.seqItems = this.seqItems.slice(-1);
            }
        }
    }
    initExpandeds() {
        let vals = valToArr(this.options.expand.value).map((k) => findItem(k, this.flatData)).filter(Boolean), items = this.flatData.filter((k) => k.expanded && k.children), tmp = [...items, ...vals];
        for (let k of items)
            k.expanded = false;
        if (this.options.expand.all) {
            if (this.options.expand.only) {
                let firsts = this.getParentsGroupBy(this.flatData);
                for (let k in firsts) {
                    firsts[k][0] && super.expand(firsts[k][0]);
                }
            }
            else {
                super.expandAll();
            }
        }
        else {
            if (this.options.expand.only) {
                let firsts = this.getParentsGroupBy(tmp);
                for (let k in firsts) {
                    firsts[k][0] && super.expand(firsts[k][0]);
                }
            }
            else {
                super.expand(tmp);
            }
        }
    }
    initSelecteds() {
        if (!this.options.select.enable)
            return;
        let vals = valToArr(this.options.select.value).map((k) => findItem(k, this.flatData)).filter(Boolean), items = this.flatData.filter((k) => k.selected), tmp = [...items, ...vals];
        for (let k of items)
            k.selected = false;
        this.select(this.options.select.only ? tmp[0] : tmp);
    }
    getSelecteds() {
        return this.flatData.filter((k) => k.selected);
    }
    getParentsGroupBy(items, key = 'pId') {
        if (isEmpty(items))
            return [];
        return items.reduce((result, item) => {
            let groupKey = item[key];
            if (!result[groupKey]) {
                result[groupKey] = [];
            }
            item.children && result[groupKey].push(item);
            return result;
        }, {});
    }
    getIndentHtml(floor) {
        return '<i></i>'.repeat(floor);
    }
    updateChildrenFloor(item) {
        if (item.children) {
            for (let k of item.children) {
                k.floor = item.floor + 1;
                this.updateChildrenFloor(k);
            }
        }
    }
    async renderData(data) {
        let outer = createEl('ul');
        let plantTree = async (parent, data) => {
            let ul = createEl('ul', { class: `${prefix}reset ${prefix}tree-children` });
            for (let k of data) {
                this.createHeadEl(k, parent);
                await this.createBodyEl(k);
                if (k.hasOwnProperty('children')) {
                    k.childrenEl = await plantTree(k, k.children);
                }
                
                ((!this.options.deferred && !this.options.paginated.enable)
                    ||
                        (this.options.deferred && !this.options.paginated.enable && k.floor === this.options.floorStart)
                    ||
                        (this.options.paginated.enable && k.floor === this.options.floorStart && this.options.paginated.exception))
                    && ul.appendChild(k.wrapEl);
            }
            (parent.wrapEl || parent).appendChild(ul);
            parent.wrapEl && this.createFootEl(parent);
            return ul;
        };
        await plantTree(outer, data);
        this.resultEl = createEl('div', { class: `${prefix}tree-result` });
        this.options.output.enable && !this.options.output.instant && this.targetEl.appendChild(this.resultEl);
        if (this.options.name) {
            this.inputEl = createEl('input', { type: 'hidden', name: this.options.name });
            this.targetEl.appendChild(this.inputEl);
        }
        this.pagination.childrenEl = outer.childNodes[0];
        super.listen({ name: 'insertItems', params: [data] });
        this.targetEl.appendChild(this.pagination.childrenEl);
        if (this.options.paginated.enable && !this.options.paginated.exception) {
            this.createFootEl(this.pagination);
        }
    }
    updateArrowEl(item) {
        if (!this.options.arrow.enable)
            return;
        if (item.hasOwnProperty('children')) {
            item.arrowEl.setAttribute('type', this.options.arrow.type);
            this.options.lang.title.arrow && item.arrowEl.setAttribute('title', this.options.lang.title.arrow);
            if (this.options.arrow.type === 'image') {
                this.options.arrow.hide && (item.arrowEl.style.backgroundImage = `url("${this.options.arrow.hide}")`);
            }
            else {
                item.arrowEl.classList.remove(`${prefix}none`);
                this.options.arrow.hide && item.arrowEl.classList.add(this.options.arrow.hide);
            }
        }
        else {
            item.arrowEl.removeAttribute('type');
            item.arrowEl.removeAttribute('title');
            if (this.options.arrow.type === 'image') {
                item.arrowEl.removeAttribute('style');
            }
            else {
                classes(item.arrowEl).remove([this.options.arrow.show, this.options.arrow.hide]).add(`${prefix}none`);
            }
        }
    }
    async setBrief(target, data = {}, cb) {
        if (this.destroyed)
            return;
        let item = findItem(target, this.flatData);
        await this.createBodyEl(item, false);
        await super.setElCont({
            item,
            data,
            cb: (cont, target) => {
                super.listen({ name: 'filled', cb, params: [cont, target] });
            }
        });
        return this;
    }
    setLegendVal(el, val, type = 'icon') {
        if (type === 'image') {
            el.removeAttribute('class');
            el.style.backgroundImage = `url("${val}")`;
        }
        else {
            el.removeAttribute('style');
            el.setAttribute('class', val);
        }
    }
    toggleParentLegend(item) {
        if (!this.options.legend.enable || item.legend || !Array.isArray(this.options.legend.parent) || !this.options.legend.parent[1])
            return;
        this.setLegendVal(item.legendEl, this.options.legend.parent[item.expanded ? 1 : 0], this.options.legend.type);
    }
    updateLegendEl(item) {
        if (!this.options.legend.enable)
            return;
        if (item.legend) {
            this.setLegendVal(item.legendEl, item.legend, this.options.legend.type);
        }
        else {
            if (item.hasOwnProperty('children')) {
                this.setLegendVal(item.legendEl, this.options.legend.parent[item.expanded ? 1 : 0], this.options.legend.type);
            }
            else {
                this.setLegendVal(item.legendEl, this.options.legend.child, this.options.legend.type);
            }
        }
    }
    getLegendEl(item) {
        item.legendEl = createEl('i', { [alias]: 'legend' });
        this.updateLegendEl(item);
    }
    createHeadEl(item, parent) {
        if (item.wrapEl)
            return;
        item.wrapEl = createEl('li', { class: `${prefix}tree-wrap` });
        item.groupEl = createEl('div', { [alias]: 'group' });
        if (item.headEl) {
            if (!item.labelEl) {
                item.labelEl = createEl(item.hasOwnProperty('href') ? 'a' : 'i', { [alias]: 'label' }, item.label);
                item.headEl.appendChild(item.labelEl);
            }
        }
        else {
            item.headEl = createEl('div', { class: `${prefix}tree-head` });
            !item.labelEl && (item.labelEl = createEl(item.hasOwnProperty('href') ? 'a' : 'i', { [alias]: 'label' }, item.label));
            item.headEl.appendChild(item.labelEl);
        }
        item.wrapEl.appendChild(item.headEl);
        item.href && (item.labelEl.href = item.href);
        item.target && (item.labelEl.target = item.target);
        item.indentHeadEl = createEl('span', { [alias]: 'indent' });
        item.indentBodyEl = createEl('span', { [alias]: 'indent' });
        item.indentFootEl = createEl('span', { [alias]: 'indent' });
        item.indentHeadEl.innerHTML = item.indentBodyEl.innerHTML = item.indentFootEl.innerHTML = this.getIndentHtml(item.floor);
        this.options.arrow.enable && super.getArrowEl(item);
        let imgNone = getImgNone();
        if (!item.iconEl) {
            item.iconEl = item.hasOwnProperty('icon') ? createEl('i', { [alias]: 'icon', class: item.icon }) : null;
        }
        if (!item.diskEl) {
            item.diskEl = item.hasOwnProperty('disk') ? createEl('img', { [alias]: 'disk', src: item.disk || imgNone }) : null;
        }
        if (!item.cubeEl) {
            item.cubeEl = item.hasOwnProperty('cube') ? createEl('img', { [alias]: 'cube', src: item.cube || imgNone }) : null;
        }
        if (!item.imageEl) {
            item.imageEl = item.hasOwnProperty('image') ? createEl('img', { [alias]: 'image', src: item.image || imgNone }) : null;
        }
        if (!item.badgeEl) {
            item.badgeEl = item.badge ? createEl('ax-badge', { [alias]: 'badge', label: item.badge.toString().trim() }) : null;
        }
        if (!item.tipsEl) {
            item.tipsEl = item.tips ? createEl('i', { [alias]: 'tips' }, item.tips) : null;
        }
        if (!item.customEl) {
            item.customEl = item.custom ? createEl('div', { [alias]: 'custom' }, item.custom) : null;
        }
        item.expanded && item.headEl.toggleAttribute('expanded', true);
        item.selected && item.headEl.toggleAttribute('selected', true);
        item.disabled && item.headEl.toggleAttribute('disabled', true);
        item.readonly && item.headEl.toggleAttribute('readonly', true);
        item.checked && item.headEl.toggleAttribute('checked', true);
        this.options.drag.enable && item.headEl.toggleAttribute([this.dropTag], true);
        if (this.options.check.enable) {
            item.checkType = this.getCheckType(parent);
            item.checkEl = createEl(`ax-${item.checkType}`, { [alias]: 'check' });
        }
        if (this.options.legend.enable) {
            this.getLegendEl(item);
        }
        if (this.options.tools.enable) {
            item.toolsEl = createTools(this.options.tools.children, item.headEl);
            item.toolsEl.setAttribute(alias, 'tools');
            item.tools = deepClone(this.options.tools.children);
            for (let k of item.tools) {
                this.options.lang.title[k.name] && k.wrapEl.setAttribute('title', this.options.lang.title[k.name]);
                if (k.name === 'file') {
                    item.addfileEl = k.wrapEl;
                }
                else if (k.name === 'folder') {
                    item.addfolderEl = k.wrapEl;
                }
                else if (k.name === 'remove') {
                    item.removeEl = k.wrapEl;
                }
                else if (k.name === 'edit') {
                    item.editEl = k.wrapEl;
                }
            }
        }
        super.parseLayout(item.headEl, this.options.layout, {
            indent: item.indentHeadEl,
            arrow: item.arrowEl,
            label: item.labelEl,
            check: item.checkEl,
            legend: item.legendEl,
            icon: item.iconEl,
            disk: item.diskEl,
            cube: item.cubeEl,
            image: item.imageEl,
            badge: item.badgeEl,
            tips: item.tipsEl,
            custom: item.customEl,
            tools: item.toolsEl,
            group: item.groupEl,
        });
    }
    async createBodyEl(item, autoFill = this.options.autoFill) {
        if (item.bodyEl)
            return;
        item.bodyEl = createEl('div', { class: `${prefix}tree-body` });
        item.bodyEl.appendChild(item.indentBodyEl);
        item.indentBodyEl.insertAdjacentHTML('afterend', `<i  class="${prefix}none"></i>`);
        if (!item.briefEl) {
            item.briefEl = createEl('div', { class: `${prefix}tree-brief` });
            if (autoFill) {
                
                await super.getElCont({
                    target: item,
                    data: item.brief,
                    cb: (cont) => {
                        item.brief = cont;
                        super.updateElCont(item);
                    }
                });
            }
        }
        item.bodyEl.appendChild(item.briefEl);
        item.headEl.insertAdjacentElement('afterend', item.bodyEl);
    }
    createFootEl(item) {
        if (!this.options.paginated.enable || item.pageEl)
            return;
        item.moreEl = createEl('i', { [alias]: 'more' }, this.options.lang.paginated.more);
        item.nextEl = createEl('i', { [alias]: 'next' }, this.options.lang.paginated.next);
        item.firstEl = createEl('i', { [alias]: 'first' }, this.options.lang.paginated.first);
        item.infoEl = createEl('i', { [alias]: 'info' });
        item.pageEl = createEl('div', { class: `${prefix}tree-page` }, item.infoEl);
        this.options.paginated.override ? item.pageEl.append(item.nextEl, item.firstEl) : item.pageEl.appendChild(item.moreEl);
        item.footEl = createEl('div', { class: `${prefix}tree-foot` });
        if (item !== this.pagination) {
            item.footEl.appendChild(item.indentFootEl);
            item.indentFootEl.insertAdjacentHTML('afterend', `<i  class="${prefix}none"></i>`);
        }
        item.footEl.appendChild(item.pageEl);
        item.children && item.wrapEl.appendChild(item.footEl);
    }
    togglePgnDisabled(item) {
        if (this.options.paginated.enable) {
            item.moreEl.toggleAttribute('disabled', item.atEnd);
            item.nextEl.toggleAttribute('disabled', item.atEnd);
            item.firstEl.toggleAttribute('disabled', item.current == 1 ? true : false);
        }
    }
    turnNextPage(item, cb) {
        if (this.destroyed)
            return;
        let target = item === this.pagination ? this.pagination : findItem(item, this.flatData);
        if (!target)
            return;
        if (!this.options.paginated.enable || !target.children)
            return;
        let pages = Math.ceil(target.children.length / this.options.paginated.count);
        if (this.options.paginated.override) {
            if (target.current >= pages)
                return;
            target.current = target.current || 1;
            let nextData = target.children.slice(target.current * this.options.paginated.count, (target.current + 1) * this.options.paginated.count);
            target.childrenEl.innerHTML = '';
            target.childrenEl.append(...nextData.map((k) => k.wrapEl));
            target.current++;
        }
        else {
            if (target.childrenEl.children.length >= target.children.length)
                return;
            let curCount = target.childrenEl.children.length, nextData = target.children.slice(curCount, curCount + this.options.paginated.count);
            target.childrenEl.append(...nextData.map((k) => k.wrapEl));
            target.current = Math.ceil(target.childrenEl.children.length / this.options.paginated.count);
        }
        target.atEnd = (pages === target.current) ? true : false;
        this.togglePgnDisabled(target);
        this.updateInfoEl(target);
        super.listen({ name: 'turned', cb, params: [target] });
        return this;
    }
    turnFirstPage(item, cb) {
        if (this.destroyed)
            return;
        let target = item === this.pagination ? this.pagination : findItem(item, this.flatData);
        if (!target)
            return;
        if (!this.options.paginated.enable || !target.children)
            return;
        let firstData = target.children.slice(0, this.options.paginated.count);
        this.options.paginated.override && (target.childrenEl.innerHTML = '');
        target.childrenEl.append(...firstData.map((k) => k.wrapEl));
        target.current = 1;
        target.atEnd = (target.childrenEl.children.length === target.children.length) ? true : false;
        this.togglePgnDisabled(target);
        this.updateInfoEl(target);
        super.listen({ name: 'turned', cb, params: [target] });
        return this;
    }
    updateInfoEl(item) {
        if (!this.options.paginated.enable)
            return;
        let total = item.children.length, pages = Math.ceil(total / this.options.paginated.count), current = item.current, rest = Math.max(total - current * this.options.paginated.count, 0), obj = { total, current, pages, rest, count: this.options.paginated.count, label: item.label, id: item.id };
        item.infoEl.innerHTML = renderTpl(this.options.lang.paginated.info, obj);
    }
    createChildrenEl(item) {
        if (item.childrenEl)
            return;
        item.childrenEl = createEl('ul', { class: `${prefix}reset ${prefix}tree-children` });
        let target = item.hasOwnProperty('brief') ? item.bodyEl : item.headEl;
        target.insertAdjacentElement('afterEnd', item.childrenEl);
    }
    child2Parent(item) {
        this.createChildrenEl(item);
        this.updateArrowEl(item);
        this.updateLegendEl(item);
        this.updateEvt(item);
    }
    parent2Child(item) {
        item.childrenEl.remove();
        this.updateArrowEl(item);
        this.updateLegendEl(item);
        this.updateEvt(item);
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}tree`);
        this.options.classes && classes(this.targetEl).add(this.options.classes);
        this.targetEl.toggleAttribute('inert', this.options.passive);
        this.targetEl.setAttribute('tools-trigger', this.options.tools.trigger);
        this.targetEl.toggleAttribute('chain', this.options.chain);
        this.options.feature.type && this.targetEl.setAttribute('feature', this.options.feature.type);
        this.options.check.enable ? this.targetEl.setAttribute('check', this.options.check.span) : this.targetEl.removeAttribute('check');
        this.options.select.enable ? this.targetEl.setAttribute('select', this.options.select.span) : this.targetEl.removeAttribute('select');
    }
    setDragDrop(item) {
        if (!this.options.drag.enable || item.disabled || this.excludeDrags.includes(item))
            return;
        item.dragIns = new Drag(item.wrapEl, extend({
            target: {
                drops: `[${this.dropTag}]`,
                handle: item.headEl,
                purpose: 'auto',
                parent: !this.options.drop.global ? this.targetEl : null,
                gesture: {
                    unbound: [item.toolsEl]
                },
                onDropped: (data) => {
                    if (data.point === 'before') {
                        this.graft({ source: data.from, target: data.to, isFront: true, isChild: false });
                    }
                    else if (data.point === 'after') {
                        this.graft({ source: data.from, target: data.to, isFront: false, isChild: false });
                    }
                    else if (data.point === 'inside') {
                        this.graft({ source: data.from, target: data.to, isFront: true, isChild: true });
                    }
                }
            },
            source: this.options.drag,
        }));
    }
    updateExpandEvt(k) {
        if (k.children) {
            if (k.arrowEl) {
                if (this.options.arrow.trigger === 'click') {
                    k.arrowEl.removeEventListener('click', this.expandEvt);
                    k.arrowEl.addEventListener('click', this.expandEvt, false);
                }
                else if (this.options.arrow.trigger === 'hover') {
                    k.hoverIns && k.hoverIns.destroy();
                    k.hoverIns = new Hover(k.arrowEl, {
                        onMove: (isIn) => {
                            !isIn && this.options.expand.only ? super.collapse(k) : null;
                        },
                        onEnter: () => {
                            super.expand(k);
                        },
                        onLeave: () => {
                            super.collapse(k);
                        }
                    });
                }
            }
            if (k.legendEl) {
                k.legendEl.removeEventListener('click', this.expandEvt);
                k.legendEl.addEventListener('click', this.expandEvt, false);
            }
        }
        else {
            if (this.options.arrow.trigger === 'click') {
                k.arrowEl && k.arrowEl.removeEventListener('click', this.expandEvt);
            }
            else if (this.options.arrow.trigger === 'hover') {
                k.hoverIns && k.hoverIns.destroy();
            }
            k.legendEl && k.legendEl.removeEventListener('click', this.expandEvt);
        }
    }
    updateEvt(k) {
        if (this.options.paginated.enable && k.children) {
            this.turnFirstPage(k);
            k.moreEl.onclick = () => this.turnNextPage(k);
            k.nextEl.onclick = k.moreEl.onclick;
            k.firstEl.onclick = () => this.turnFirstPage(k);
        }
        this.updateExpandEvt(k);
        k.labelEl.removeEventListener('click', this.selectEvt);
        k.labelEl.addEventListener('click', this.selectEvt, false);
        if (k.toolsEl) {
            if (k.removeEl) {
                k.removeEl.onclick = () => {
                    if (k.readonly)
                        return;
                    this.remove(k);
                };
            }
            if (k.editEl) {
                k.editEl.onclick = () => {
                    if (k.readonly)
                        return;
                    this.inputLabel(k);
                };
            }
            if (k.addfileEl) {
                k.addfileEl.onclick = () => {
                    if (k.readonly)
                        return;
                    this.add({ target: k, isChild: true, expand: true });
                };
            }
            if (k.addfolderEl) {
                k.addfolderEl.onclick = () => {
                    if (k.readonly)
                        return;
                    this.add({ target: k, isChild: true, isLeaf: false, expand: true });
                };
            }
            for (let [i, o] of this.options.tools.children.entries()) {
                let tool = k.tools[i], refer = { ins: this, item: k };
                o.action && o.action.call(refer, tool);
                tool.action && tool.action.call(refer, tool);
            }
        }
        if (this.options.shortcut.enable) {
            k.headEl.removeEventListener('click', this.lineEvt);
            k.headEl.addEventListener('click', this.lineEvt, false);
        }
        k.disabled && k.headEl.toggleAttribute('disabled', true);
        k.readonly && k.headEl.toggleAttribute('readonly', true);
        if (this.options.check.enable) {
            k.checkEl.on('check', async (val) => {
                !this.chainChecking && val.checked !== k.checked && await this.check(k, val.checked);
            });
        }
        if (this.options.drag.enable) {
            !k.dragIns && this.setDragDrop(k);
        }
        if (this.options.check.enable) {
            if (this.options.check.span === 'leaf') {
                k.children && (k.checkEl.toggleAttribute('disabled', true), k.headEl.toggleAttribute('uncheckable', true));
            }
            else if (this.options.check.span === 'branch') {
                !k.children && (k.checkEl.toggleAttribute('disabled', true), k.headEl.toggleAttribute('uncheckable', true));
            }
        }
        if (this.options.select.enable) {
            if (this.options.select.span === 'leaf') {
                k.children && k.headEl.toggleAttribute('unselectable', true);
            }
            else if (this.options.select.span === 'branch') {
                !k.children && k.headEl.toggleAttribute('unselectable', true);
            }
        }
        k.action && k.action.call(this, k);
    }
    renderFinish() {
        for (let k of this.flatData) {
            this.updateEvt(k);
        }
        if (this.options.paginated.enable && !this.options.paginated.exception) {
            this.turnFirstPage(this.pagination);
            this.pagination.moreEl.onclick = () => this.turnNextPage(this.pagination);
            this.pagination.nextEl.onclick = this.pagination.moreEl.onclick;
            this.pagination.firstEl.onclick = () => this.turnFirstPage(this.pagination);
        }
    }
    getDrops() {
        return isEmpty(this.options.drag.drops) ? this.flatData.map((k) => !k.disabled ? k.headEl : null).filter(Boolean) :
            this.options.drag.value.map((k) => findItem(k, this.flatData)).filter((k) => k && !k.disabled).map((k) => k.headEl);
    }
    getReadonly() {
        return this.flatData.find((k) => k.readonly);
    }
    getTriggerEl(item) {
        return (item.href && item.arrowEl ? item.arrowEl : item.headEl);
    }
    addTrigger(item, target) {
        if (item.children) {
            if (!item.disabled) {
                let triggerEl = target || this.getTriggerEl(item);
                if (this.options.arrow.trigger === 'click') {
                    triggerEl.removeEventListener('click', this.expandEvt);
                    triggerEl.addEventListener('click', this.expandEvt, false);
                }
                else if (this.options.arrow.trigger === 'hover') {
                    this.hoverIns ? this.hoverIns.destroy() : null;
                    this.hoverIns = new Hover(item.wrapEl, {
                        onEnter: () => {
                            super.expand(item);
                            super.listen({ name: 'trigger', params: [item] });
                        },
                        onLeave: () => {
                            super.collapse(item);
                        }
                    });
                }
            }
        }
        else {
            if (!item.disabled) {
                item.headEl.removeEventListener('click', this.selectEvt);
                item.headEl.addEventListener('click', this.selectEvt, false);
            }
        }
    }
    removeTrigger(item, target) {
        if (item.children) {
            if (!item.disabled) {
                let triggerEl = target || this.getTriggerEl(item);
                if (this.options.arrow.trigger === 'click') {
                    triggerEl.removeEventListener('click', this.expandEvt);
                }
                else if (this.options.arrow.trigger === 'hover') {
                    this.hoverIns ? this.hoverIns.destroy() : null;
                }
            }
        }
        else {
            if (!item.disabled) {
                item.headEl.removeEventListener('click', this.selectEvt);
            }
        }
    }
    toggleExpanded(data) {
        if (this.destroyed)
            return;
        let item = findItem(data, this.flatData);
        if (!item || !item.children)
            return;
        item.expanded ? super.collapse(item) : super.expand(item);
        super.listen({ name: 'trigger', params: [item] });
    }
    toggleSelected(data) {
        let item = findItem(data, this.flatData);
        if (!item || item.headEl.hasAttribute('editing') || !this.options.select.enable)
            return;
        if (item.selected) {
            this.deselect(item);
        }
        else {
            this.select(item);
            this.options.select.only && this.deselect(this.flatData.filter((k) => k !== item && k.selected));
        }
    }
    async eachCollapse(data, cb) {
        if (isNull(data))
            return;
        try {
            if (this.options.b4Collapse) {
                let resp = await this.options.b4Collapse.call(this, data);
                resp && (data = resp);
            }
        }
        catch (err) {
            err ? console.error(err) : console.warn('Branch collapsing has been prevented!');
            return;
        }
        let item = findItem(data, this.flatData);
        if (!item || !item.children || !elState(item.childrenEl).isVisible || item.childrenEl.style.height)
            return;
        super.listen({ name: 'collapse', cb, params: [item] });
        item.expanded = false;
        slideUp({
            el: item.childrenEl,
            duration: this.options.duration,
            done: () => {
                super.listen({ name: 'collapsed', cb, params: [item] });
            }
        });
    }
    async eachExpand(data, cb) {
        if (isNull(data))
            return;
        let item = findItem(data, this.flatData);
        if (!item || !item.children || elState(item.childrenEl).isVisible)
            return;
        try {
            if (this.options.b4Expand) {
                let resp = await this.options.b4Expand.call(this, data);
                resp && (data = resp);
            }
        }
        catch (err) {
            err ? console.error(err) : console.warn('Branch expansion has been prevented!');
            return;
        }
        super.listen({ name: 'expand', cb, params: [item] });
        if (!item.children.length && !isEmpty(this.options.contData) && this.options.contType === 'async') {
            await this.sqlToAdd(item);
        }
        else {
            if (this.options.deferred && !item.childrenEl.firstElementChild) {
                item.arrowEl && item.arrowEl.toggleAttribute('spinning', true);
                this.options.paginated.enable ? this.turnFirstPage(item) : item.childrenEl.append(...item.children.map((k) => k.wrapEl));
                item.arrowEl && item.arrowEl.removeAttribute('spinning');
            }
        }
        item.expanded = true;
        this.options.expand.linkage && this.expandParents(item);
        this.lastExpanded = item.id;
        slideDown({
            el: item.childrenEl,
            duration: this.options.duration,
            done: () => {
                super.listen({ name: 'expanded', cb, params: [item] });
            }
        });
        if (this.options.expand.only) {
            let others = this.flatData.filter((k) => (k !== item && k.expanded && k.floor === item.floor && k.children));
            for (let k of others)
                super.collapse(k);
        }
    }
    floatDown(obj) {
        if (!obj.hasOwnProperty('children') || !Array.isArray(obj.children))
            return;
        let enables = obj.children.filter((k) => !k.disabled), type = this.getCheckType(obj);
        for (let k of enables) {
            if (obj.checked) {
                type === 'checkbox' && (k.checked = true);
            }
            else {
                k.checked = false;
            }
            k.hasOwnProperty('children') && this.floatDown(k);
        }
    }
    floatUp(obj) {
        if (obj.floor === this.options.floorStart)
            return;
        let parents = treeTools.getParentsFromPath({ path: obj.path, flatData: this.flatData, pathHyphen: this.options.pathHyphen, pop: true }).parents.reverse(), setCheck = (item, enables, type) => {
            if (type === 'checkbox') {
                if (enables.every((k) => !k.checked)) {
                    item.checked = false;
                    item.checkEl.setAttribute('check', '');
                }
                else if (enables.every((k) => k.checked)) {
                    item.checked = true;
                    item.checkType === 'radio' && this.uncheckSibings(item);
                }
                else {
                    item.checked = false;
                    item.checkEl.setAttribute('check', 'ing');
                }
            }
            else if (type === 'radio') {
                if (enables.some((k) => k.checked)) {
                    item.checked = true;
                    item.checkType === 'radio' && this.uncheckSibings(item);
                }
                else {
                    item.checked = false;
                    item.checkEl.setAttribute('check', item.children.some((k) => k.checkEl.properties.check === 'ing') ? 'ing' : '');
                }
            }
        };
        for (let i of parents) {
            let enables = i.children.filter((k) => !k.disabled), type = this.getCheckType(i);
            setCheck(i, enables, type);
        }
    }
    eachCheck(item) {
        this.chainChecking = true;
        this.floatDown(item);
        this.floatUp(item);
        this.chainChecking = false;
    }
    getCheckType(parent) {
        return parent?.childType || (['checkbox', 'radio'].includes(this.options.check.type) ? this.options.check.type : 'checkbox');
    }
    uncheckSibings(item) {
        this.chainChecking = true;
        let siblings = super.getSiblings(item).filter((k) => k.checked);
        for (let k of siblings) {
            k.checked = false;
            this.options.check.linkage && this.floatDown(k);
        }
        this.chainChecking = false;
    }
    expandParents(data, cb) {
        let tmp = findItems(data, this.flatData), items = tmp.map((k) => findItem(k, this.flatData)).filter(Boolean), pSet = [];
        if (items.length) {
            let parents = [];
            for (let k of items) {
                let p = treeTools.getParentsFromPath({ path: k.path, flatData: this.flatData, field: 'id', pathHyphen: this.options.pathHyphen }).parents;
                p.pop();
                parents.push(...p);
            }
            pSet = unique(parents);
            for (let k of pSet)
                super.expand(k);
        }
        cb && cb(pSet);
    }
    select(data, flag = true, cb) {
        if (this.destroyed || !this.options.select.enable || isNull(data))
            return;
        let tmp = findItems(data, this.flatData), items = tmp.map((k) => findItem(k, this.flatData)).filter((k) => !k.selected === flag && (this.options.select.span === 'leaf' ? !k.children : this.options.select.span === 'branch' ? k.children : true)), param = items, fn = (obj) => {
            if (obj.headEl.hasAttribute('editing') || obj.headEl.hasAttribute('unselectable') || obj.selected)
                return;
            obj.selected = true;
        };
        if (!items.length)
            return;
        if (flag) {
            if (this.options.select.only) {
                let item = items[0];
                if (item) {
                    fn(item);
                    this.deselect(this.flatData.filter((k) => k !== item && k.selected));
                }
                param = [item];
            }
            else {
                this.saveRaw();
                for (let k of items)
                    fn(k);
            }
            this.options.select.linkage && this.expandParents(param);
        }
        else {
            this.deselect(items);
        }
        super.listen({ name: flag ? 'selected' : 'deselected', cb, params: [param] });
        return this;
    }
    selectAll(cb) {
        if (this.destroyed || !this.options.select.enable || this.options.select.only)
            return;
        this.select(this.flatData);
        super.listen({ name: 'selectedAll', cb });
        return this;
    }
    deselectAll(cb) {
        if (this.destroyed || !this.options.select.enable)
            return;
        this.select(this.flatData, false);
        super.listen({ name: 'deselectedAll', cb });
        return this;
    }
    deselect(data) {
        if (isNull(data))
            return;
        let items = Array.isArray(data) ? data : [data], fn = (obj) => {
            if (!obj.selected)
                return;
            obj.selected = false;
        };
        this.saveRaw();
        for (let k of items)
            fn(k);
    }
    checkOnlyone(item) {
        this.saveRaw();
        item.checked = true;
        this.chainChecking = true;
        let others = this.flatData.filter((k) => k !== item);
        for (let k of others) {
            k.checked = false;
            k.checkEl && k.checkEl.removeAttribute('check');
        }
        this.chainChecking = false;
    }
    async check(data, flag = true, cb) {
        if (this.destroyed)
            return;
        let filters = findItems(data, this.flatData), items = filters.filter((k) => !k.checked === flag && (flag ? (this.options.check.span === 'leaf' ? !k.children : this.options.check.span === 'branch' ? !!k.children : true) : true)), fn = (obj) => {
            obj.checked = flag;
            flag && obj.checkType === 'radio' && this.uncheckSibings(obj);
            this.options.check.linkage && this.eachCheck(obj);
        };
        if (!items.length)
            return;
        if (this.options.check.max) {
            try {
                let param = { data: items, source: this.getCheckeds(), min: this.options.check.min, max: this.options.check.max, sliced: this.options.check.sliced }, resp = await (flag ? this.moreExceed(param) : this.lessExceed(param));
                resp && (items = resp);
            }
            catch {
                return;
            }
        }
        if (flag && this.options.check.only) {
            this.checkOnlyone(items[0]);
        }
        else {
            this.saveRaw();
            for (let k of items)
                fn(k);
        }
        
        super.listen({ name: flag ? 'checked' : 'unchecked', cb, params: [items] });
        return this;
    }
    checkAll(cb) {
        if (this.destroyed || !this.options.check.enable || this.options.check.type !== 'checkbox' || this.options.check.only)
            return;
        this.saveRaw();
        for (let k of this.flatData) {
            k.checked = true;
        }
        super.listen({ name: 'checkedAll', cb });
        return this;
    }
    async add(options) {
        if (this.destroyed)
            return;
        
        let opts = Object.assign({ isChild: true, isFront: true, repeat: true, isLeaf: true, autoFill: true, expand: true, }, options), type = getDataType(opts.data), target = findItem(opts.target, this.flatData), brother = findItem(opts.brother, this.flatData), tmp = [], items = [], appendFun = async (data) => {
            let obj = treeTools.createBranchObj({
                source: data,
                flatData: this.flatData,
                target,
                isLeaf: opts.isLeaf,
                isChild: opts.isChild
            });
            this.createHeadEl(obj);
            await this.createBodyEl(obj);
            this.createFootEl(obj);
            this.createChildrenEl(obj);
            super.listen({ name: 'insertItem', params: [obj] });
            treeTools.addBranch({
                source: obj,
                rootEl: this.targetEl,
                flatData: this.flatData,
                treeData: this.treeData,
                brother: target && !opts.isChild ? target : brother,
                isFront: opts.isFront,
                repeat: opts.repeat,
                autoFill: opts.autoFill,
                cb: (item) => {
                    items.push(item);
                    this.updateEvt(item);
                }
            });
        };
        tmp = isNull(opts.data) ? [null] : ((type === 'Array' && opts.data.length > 0) ? opts.data : [opts.data]);
        try {
            let resp = await super.moreExceed({ data: tmp, source: this.flatData });
            resp && (tmp = resp);
        }
        catch {
            return;
        }
        for (let k of tmp) {
            try {
                if (this.options.b4Add) {
                    let resp = await this.options.b4Add.call(this, k);
                    resp && (k = resp);
                }
            }
            catch (err) {
                err ? console.error(err) : console.warn(`Adding new branche (${JSON.stringify(k)}) has been prevented!`);
                continue;
            }
            await appendFun(k);
        }
        if (items.length > 0) {
            this.flatData = treeTools.toFlat(this.treeData);
            opts.isChild && opts.expand && target && this.eachExpand(target);
            if (this.options.select.enable && this.options.select.addSelected) {
                if (!this.options.select.only) {
                    for (let k of items)
                        this.select(k);
                }
                else {
                    this.select(items.at(-1));
                }
            }
        }
        super.listen({ name: 'added', cb: opts.cb, params: [items, target] });
        return this;
    }
    async remove(data, cb) {
        if (this.destroyed || isNull(data) || !this.flatData.length)
            return;
        if (this.flatData.length === 0) {
            console.warn('The source data is already empty!');
            return;
        }
        let tmp = [], items = [], removeItem = (child) => {
            treeTools.removeBranch({
                source: child,
                flatData: this.flatData,
                treeData: this.treeData,
                remove: false,
                cb: (obj) => {
                    items.push(obj);
                    setTimeout(() => {
                        let parent = this.flatData.find((k) => k.id === obj.pId);
                        parent && !parent.children.length && super.collapse(parent);
                    }, 0);
                }
            });
        };
        tmp = findItems(data, this.flatData);
        try {
            let resp = await super.lessExceed({ data: tmp, source: this.flatData });
            resp && (tmp = resp);
        }
        catch {
            return;
        }
        for (let k of tmp) {
            try {
                if (this.options.b4Remove) {
                    let resp = await this.options.b4Remove.call(this, k);
                    resp && (k = resp);
                }
            }
            catch (err) {
                err ? console.error(err) : console.warn(`Removing old branch (${JSON.stringify(k)}) has been prevented!`);
                continue;
            }
            removeItem(k);
        }
        this.flatData = treeTools.toFlat(this.treeData);
        super.listen({ name: 'removed', cb, params: [items] });
        return this;
    }
    search(value, opts = {}, cb) {
        if (this.destroyed || !this.flatData.length)
            return;
        let hyphen = config.splitHyphen;
        if (typeof value === 'string') {
            let tmp = value.trim();
            hyphen = tmp.includes('，') ? '，' : tmp.includes(',') ? ',' : tmp.includes(' ') ? ' ' : config.splitHyphen;
        }
        let keys = valToArr(value, hyphen, false).map((k) => clearRegx(k)).filter(Boolean);
        if (!keys.length) {
            for (let k of this.searchs) {
                k.labelEl.innerHTML = k.label;
            }
            if (!isEmpty(this.ignores)) {
                for (let k of this.ignores)
                    k.wrapEl.classList.remove(`${prefix}d-none`);
            }
            this.searchs = [];
            this.ignores = [];
        }
        else {
            let tmp = this.searchs;
            this.searchs = arrSearch(Object.assign({ keys, props: 'label', source: this.flatData }, { fuzzy: this.options.search.fuzzy, ignore: this.options.search.ignore }, opts)).map((k) => k.source);
            for (let k of this.searchs) {
                k.labelEl.innerHTML = super.replaceMult(k.label, keys);
            }
            if (!isEmpty(tmp)) {
                for (let k of tmp) {
                    if (!this.searchs.includes(k)) {
                        k.labelEl.innerHTML = k.label;
                    }
                }
            }
            this.expandParents(this.searchs, (p) => {
                let currents = unique([...p, ...this.searchs]);
                this.ignores = this.flatData.filter((k) => !currents.includes(k));
                for (let k of this.ignores)
                    k.wrapEl.classList.add(`${prefix}d-none`);
                for (let k of currents)
                    k.wrapEl.classList.remove(`${prefix}d-none`);
            });
        }
        super.updateCache({ search: { value: keys } });
        super.listen({ name: 'searched', cb, params: [this.searchs, keys] });
        return this;
    }
    inputLabel(item) {
        let headEl = item.headEl, labelEl = item.labelEl;
        if (headEl.hasAttribute('editing'))
            return;
        headEl.toggleAttribute('editing', true);
        labelEl.innerHTML = '';
        labelEl.appendChild(this.editorEl);
        this.editorEl.focus();
        this.editorEl.value = item.label;
        this.editorEl.onblur = async () => {
            let value = this.editorEl.value;
            try {
                if (this.options.b4Edit) {
                    let resp = await this.options.b4Edit.call(this, { key: 'label', value }, item);
                    resp && (value = resp);
                }
                item.label = value;
                headEl.removeAttribute('editing');
            }
            catch (err) {
                item.labelEl.innerHTML = item.label;
                headEl.removeAttribute('editing');
                err ? console.error(err) : console.warn(`No changes have been made to the label property!`);
                return;
            }
            super.listen({ name: 'edited', params: [item] });
        };
        this.editorEl.onkeyup = (e) => {
            (e.code === 'Enter') && this.editorEl.blur();
        };
    }
    async edit(item, data, cb) {
        if (this.destroyed || isNull(item) || isEmpty(data) || !this.flatData.length) {
            return this;
        }
        let source = findItem(item, this.flatData);
        if (!source)
            return this;
        for (let k in data) {
            let val = data[k];
            try {
                if (this.options.b4Edit) {
                    let resp = await this.options.b4Edit.call(this, { key: k, value: val }, source);
                    resp && (val = resp);
                }
                if (k === 'action') {
                    source[k] = val.call({ ins: this, item: k }, source);
                }
                else if (['icon', 'disk', 'cube', 'href', 'target', 'rel', 'label', 'brief', 'tips', 'badge'].includes(k)) {
                    source[k] = val;
                }
            }
            catch (err) {
                err ? console.error(err) : console.warn(`No changes have been made to the ${k} property!`);
                return this;
            }
        }
        super.listen({ name: 'edited', cb, params: [source] });
        return this;
    }
    async graft({ source, target, isFront = true, isChild = true, cb }) {
        if (this.destroyed || isNull(source))
            return;
        try {
            if (this.options.b4Graft) {
                let resp = await this.options.b4Graft.call(this, source, target);
                resp && resp.source && (source = resp.source);
                resp && resp.target && (source = resp.target);
            }
        }
        catch (err) {
            err ? console.error(err) : console.warn(`The branch relationship between source and target in the tree remains unchanged!`);
            return this;
        }
        let sourceObj = findItem(source, this.flatData, '', { node: 'wrapEl' }), targetObj = findItem(target, this.flatData);
        if (!sourceObj)
            return;
        treeTools.graftBranch({
            source: sourceObj,
            target: targetObj,
            isFront,
            isChild,
            flatData: this.flatData,
            treeData: this.treeData,
            rootEl: this.targetEl,
            cb: (obj, refer) => {
                refer && refer.children && isChild && this.eachExpand(refer);
                this.flatData = treeTools.toFlat(this.treeData);
                super.listen({ name: 'grafted', cb, params: [obj, refer] });
                return this;
            }
        });
        return this;
    }
    setSearch() {
        !isEmpty(this.options.search.value) && this.search(this.options.search.value);
        this.searchEl = getEl(this.options.search.target);
        if (this.searchEl) {
            if (this.searchEl[ax.compSign]) {
                this.searchEl.on(this.options.search.trigger, this.searchEvgt);
            }
            else {
                this.searchEl.addEventListener(this.options.search.trigger, this.searchEvgt, false);
            }
        }
    }
    setResult() {
        if (!this.options.output.enable || this.options.output.instant)
            return;
        this.resultIns = this.resultIns || new Tags(this.resultEl, {
            content: '',
            size: 'sm',
            type: 'plain',
            removable: true,
            empty: { enable: true, content: this.options.lang.result },
        });
        this.resultIns.on('removed', async (items) => {
            for (let k of items) {
                let tmp = k.label.split(this.options.output.connector);
                for (let i of tmp) {
                    this.options.output.form === 'checked' ? await this.check(i, false) : this.select(i, false);
                }
            }
        });
    }
    updateVals() {
        if (!this.options.output.enable)
            return;
        let { value, items } = this.getVals();
        this.updateResult(value);
        this.options.name && (this.inputEl.value = value);
        this.output.value = value;
        this.output.items = items;
        super.listen({ name: 'output', params: [this.output] });
        if (!this.options.output.instant)
            return;
        this.setVals();
    }
    saveRaw() {
        this.output.raw = this.getVals().value;
    }
    updateResult(val) {
        this.resultIns && (val ? this.resultIns.updateCont(val.split(this.options.output.separator)) : this.resultIns.clear());
    }
    clearResult() {
        this.resultIns && this.resultIns.clear();
    }
    setVals(target = this.receiver, cb) {
        if (this.destroyed)
            return;
        let receiver = getEl(target), val = this.getVals().value;
        fieldTools.setVals({ target: receiver, value: val });
        super.listen({ name: 'set', cb, params: [val] });
        return this;
    }
    clearVals(all = false, cb) {
        if (this.destroyed || !this.flatData.length)
            return this;
        if (all) {
            super.uncheckAll();
            this.deselectAll();
        }
        else {
            this.options.output.from === 'checked' ? super.uncheckAll() : this.deselectAll();
        }
        this.value = '';
        this.clearResult();
        super.listen({ name: 'cleared', cb });
        return this;
    }
    getVals(opt = {}, cb) {
        if (this.destroyed)
            return this;
        let options = Object.assign({ ...this.options.output, isStr: true }, opt), items = this.getValItems(options.from), result = [];
        if (options.type === 'chain') {
            result = items.map((k) => {
                let tmp = treeTools
                    .getParentsFromPath({ path: k.path, flatData: this.flatData, pathHyphen: this.options.pathHyphen, labelHyphen: options.connector, field: options.field })
                    .parents
                    .map((k) => k[options.field]);
                return options.isStr ? tmp.join(options.connector) : tmp;
            });
        }
        else {
            let tmp = (options.type === 'leaf') ? items.filter((k) => !k.children) : (options.type === 'branch') ? items.filter((k) => k.children) : items;
            result = tmp.map((k) => k[options.field]);
        }
        let value = options.isStr ? result.join(options.separator) : result;
        super.listen({ name: 'got', cb, params: [value] });
        return { value, items };
    }
    getValItems(from = 'selected') {
        return !this.options.output.autosort ? this.seqItems : (from === 'checked' ? super.getCheckeds() : this.getSelecteds());
    }
    
    async updateCont(content, cb) {
        if (this.destroyed)
            return this;
        await getContent.call(this, {
            content,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: {
                xhrName: 'contXhr',
                ...this.options.ajax
            },
            request: (data) => {
                this.listen({ name: 'request', params: [data] });
            },
            cb: async (data) => {
                if (Array.isArray(data)) {
                    this.treeDataOrig = deepClone(data);
                    treeTools.addIdPath({ source: this.treeDataOrig });
                    this.targetEl.innerHTML = '';
                    await this.renderData(this.treeDataOrig);
                    this.getTreeFlat();
                    this.renderFinish();
                    super.updateCache({ content });
                    super.listen({ name: 'updatedCont', cb, params: [this.treeDataOrig] });
                }
            }
        });
        return this;
    }
    
    destroy(cb) {
        if (this.destroyed)
            return this;
        for (let k of this.flatData) {
            if (k.children) {
                if (this.options.arrow.trigger === 'click') {
                    let triggerEl = k.hasOwnProperty('href') && k.arrowEl ? k.arrowEl : k.headEl;
                    triggerEl.removeEventListener('click', this.expandEvt);
                }
                else if (this.options.arrow.trigger === 'hover') {
                    this.hoverIns.destroy();
                }
                k.legendEl && k.legendEl.removeEventListener('click', this.expandEvt);
            }
            !k.children && k.headEl.removeEventListener('click', this.selectEvt);
            this.options.shortcut.enable && k.headEl.removeEventListener('click', this.lineEvt);
            this.options.drag.enable && k.dragIns.destroy();
            if (this.options.paginated.enable && !this.options.deferred) {
                k.moreEl.onclick = null;
                k.nextEl.onclick = null;
                k.firstEl.onclick = null;
            }
        }
        this.resultIns && this.resultIns.destroy();
        if (this.searchEl) {
            if (this.searchEl[ax.compSign]) {
                this.searchEl.off(this.options.search.trigger, this.searchEvgt);
            }
            else {
                this.searchEl.removeEventListener(this.options.search.trigger, this.searchEvgt);
            }
        }
        this.contXhr && this.contXhr.abort();
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

class Menu extends ModBaseListenCache {
    options = {};
    flatData;
    treeData;
    expandEvt;
    selectEvt;
    pageEvt;
    lastExpanded;
    treeDataOrig;
    drawerHost;
    drawerIns;
    treeIns;
    hoverIns;
    contXhr;
    observeIns;
    static hostType = 'node';
    static optMaps = optMenu$1;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            host: elem,
            maps: Menu.optMaps,
            component: true,
            spread: ['arrow', 'nav']
        });
        
        
        
        
        
        
        
        
        
        
        
        
        
        let _this = this;
        this.pageEvt = function (event) {
            if (!contains(event.target, _this.targetEl)) {
                _this.treeIns.collapseAll();
            }
        };
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.setAttrs();
        this.getTree();
        await this.treeIns.init();
        this.drawerHost = getEl(this.options.drawer.host);
        if (this.drawerHost) {
            this.drawerIns = new Drawer(this.drawerHost, Object.assign({
                content: this.targetEl,
                contType: 'node',
            }, this.options.drawer));
        }
        
        super.listen({ name: 'initiated', cb });
        return this;
    }
    getTree() {
        let _this = this;
        let targetObj = {
            storName: this.options.storName ? this.options.storName + '_tree' : '',
            content: this.options.content,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: this.options.ajax,
            passive: this.options.passive,
            layout: this.options.layout,
            disable: this.options.disable,
            duration: this.options.duration,
            select: {
                value: this.options.active,
                linkage: this.options.linkage,
                span: 'leaf',
            },
            expand: {
                value: this.options.active,
                all: this.options.expose || this.options.expandAll ? true : false,
                only: this.options.multiple || this.options.expose ? false : true,
                linkage: this.options.linkage,
            },
            shortcut: {
                enable: !this.options.expose,
                span: 'blank',
                mean: this.options.trigger === 'hover' ? 'selected' : 'auto',
            },
            arrow: {
                enable: this.options.arrow.enable,
                show: this.options.arrow.icon || `${prefix}icon-${this.options.nav.enable ? 'down' : 'right'}`,
                hide: this.options.arrow.icon || `${prefix}icon-${this.options.nav.enable ? 'down' : 'right'}`,
                anim: `${prefix}rotate${this.options.nav.enable || this.options.spill ? '180' : '90'}`,
                type: 'icon',
                trigger: this.options.trigger,
            },
            onInitiated: function () {
                if (_this.options.trigger === 'click' && _this.options.pageClose) {
                    document.body.removeEventListener('click', _this.pageEvt);
                    document.body.addEventListener('click', _this.pageEvt, false);
                }
            },
            onAdded: (items, refer) => {
                this.setHover(refer);
            },
            onGrafted: (item, refer) => {
                this.setHover(refer);
            },
            onInsertItem: (item) => {
                this.options.nav.enable && this.addNavAttr(item, item.floor === this.options.floorStart);
            },
            onInsertItems: (items) => {
                let tmp = treeTools.toFlat(items);
                for (let k of tmp) {
                    this.options.nav.enable && this.addNavAttr(k, k.floor === this.options.floorStart);
                    k?.children && this.setHover(k);
                }
            }
        }, params = extend({ target: targetObj, source: this.options.tree, });
        this.treeIns && this.treeIns.initiated && this.treeIns.destroy();
        this.treeIns = new Tree(this.targetEl, params, false);
    }
    setHover(item) {
        if (this.options.trigger !== 'hover' || !item?.children)
            return;
        item.triggerIns && item.triggerIns.destroy();
        item.triggerIns = new Hover(item.wrapEl, {
            onEnter: () => {
                this.treeIns.expand(item.id);
            },
            onLeave: () => {
                this.treeIns.collapse(item.id);
            }
        });
    }
    addNavAttr(item, firstItem = false) {
        if (this.options.nav.bodyWidth) {
            item.childrenEl ? addStyle(item.childrenEl, 'width', this.options.nav.bodyWidth) : removeStyle(item.childrenEl, 'width');
        }
        if (firstItem) {
            this.options.nav.headWidth ? addStyle(item.wrapEl, 'width', this.options.nav.headWidth) : removeStyle(item.wrapEl, 'width');
            this.options.nav.marginLeft ? addStyle(item.wrapEl, 'margin-left', this.options.nav.marginLeft) : removeStyle(item.wrapEl, 'margin-left');
            this.options.nav.marginRight ? addStyle(item.wrapEl, 'margin-right', this.options.nav.marginRight) : removeStyle(item.wrapEl, 'margin-right');
        }
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}menu`);
        this.options.classes && classes(this.targetEl).add(this.options.classes);
        this.targetEl.setAttribute('theme', this.options.theme);
        this.targetEl.toggleAttribute('inert', this.options.passive);
        this.targetEl.toggleAttribute('expose', this.options.expose);
        this.targetEl.setAttribute('trigger', this.options.trigger);
        this.targetEl.toggleAttribute('spill', (this.options.spill && !this.options.nav.enable));
        this.targetEl.toggleAttribute('unpadded', this.options.unpadded);
        this.options.lamp ? this.targetEl.setAttribute('lamp', this.options.lamp) : this.targetEl.removeAttribute('lamp');
        this.targetEl.toggleAttribute('full', (this.options.full && !this.options.nav.enable));
        this.options.zIndex ? addStyle(this.targetEl, 'z-index', this.options.zIndex) : removeStyle(this.targetEl, 'z-index');
        this.targetEl.toggleAttribute('nav', this.options.nav.enable);
        this.options.nav.align ? this.targetEl.setAttribute('align', this.options.nav.align) : this.targetEl.removeAttribute('align');
    }
    activate(data, cb) {
        if (this.destroyed)
            return;
        let tmp = findItems(data, this.treeIns.flatData), branches = tmp.filter((k) => k.children), leaves = tmp.filter((k) => !k.children);
        if (leaves.length) {
            this.treeIns.select(leaves, true, () => {
                this.treeIns.expand(branches);
            });
        }
        else {
            this.treeIns.expand(branches);
        }
        super.listen({ name: 'activated', cb, params: [tmp] });
        return this;
    }
    
    async updateCont(content, cb) {
        if (this.destroyed)
            return this;
        await this.treeIns.updateCont(content, (resp) => {
            super.listen({ name: 'updatedCont', cb, params: [resp] });
        });
        return this;
    }
    
    destroy(cb) {
        if (this.destroyed)
            return this;
        this.treeIns.destroy();
        document.body.removeEventListener('click', this.pageEvt);
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

const optMenu = [
    {
        attr: 'content',
        prop: 'content',
        value: '',
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'passive',
        prop: 'passive',
        value: false,
    },
    {
        attr: 'active',
        prop: 'active',
        value: '',
    },
    {
        attr: 'disable',
        prop: 'disable',
        value: '',
    },
    {
        attr: 'trigger',
        prop: 'trigger',
        value: 'click',
    },
    {
        attr: 'duration',
        prop: 'duration',
        value: 0,
    },
    {
        attr: 'max',
        prop: 'max',
        value: 0,
    },
    {
        attr: 'embed',
        prop: 'embed',
        value: false,
    },
    {
        attr: 'slider',
        prop: 'slider',
        value: {
            enable: false,
            arrowShow: false,
            options: {
                slides: 'auto',
                scroll: {
                    drift: { mode: 'free', coef: 1 }
                }
            },
        },
    },
    {
        attr: 'menu',
        prop: 'menu',
        value: {
            enable: false,
            options: {
                columns: 4,
                size: 'lg',
            }
        },
    },
    {
        attr: 'col',
        prop: 'col',
        value: {
            enable: false,
            position: 'left',
        },
    },
    {
        attr: 'attrs',
        prop: 'attrs',
        value: {
            target: {},
            head: {},
            body: {},
        },
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: {
            target: '',
            head: '',
            body: '',
        },
    },
    {
        attr: 'divider',
        prop: 'divider',
        value: {
            enable: false,
            size: '',
        },
    },
    {
        attr: 'add-active',
        prop: 'addActive',
        value: true,
    },
    {
        attr: 'tools',
        prop: 'tools',
        value: {
            enable: false,
            children: ['close'],
        },
    },
    {
        attr: 'layout',
        prop: 'layout',
        value: 'lamp',
    },
    {
        attr: 'selector',
        prop: 'selector',
        value: {
            head: '',
            body: '',
        },
    },
    {
        attr: 'sticky',
        prop: 'sticky',
        value: 0,
    },
    {
        attr: 'b4-activate',
        prop: 'b4Activate',
        value: null,
    },
    {
        attr: 'b4-add',
        prop: 'b4Add',
        value: null,
    },
    {
        attr: 'b4-edit',
        prop: 'b4Edit',
        value: null,
    },
    {
        attr: 'b4-remove',
        prop: 'b4Remove',
        value: null,
    },
    {
        attr: 'b4-graft',
        prop: 'b4Graft',
        value: null,
    },
    {
        attr: 'on-rendered',
        prop: 'onRendered',
        value: null,
    },
    {
        attr: 'on-insertitem',
        prop: 'onInsertItem',
        value: null,
    },
    {
        attr: 'on-insertitems',
        prop: 'onInsertItems',
        value: null,
    },
    {
        attr: 'on-trigger',
        prop: 'onTrigger',
        value: null
    },
    {
        attr: 'on-added',
        prop: 'onAdded',
        value: null
    },
    {
        attr: 'on-removed',
        prop: 'onRemoved',
        value: null
    },
    {
        attr: 'on-edited',
        prop: 'onEdited',
        value: null
    },
    {
        attr: 'on-grafted',
        prop: 'onGrafted',
        value: null
    },
    {
        attr: 'on-disabled',
        prop: 'onDisabled',
        value: null
    },
    {
        attr: 'on-enabled',
        prop: 'onEnabled',
        value: null
    },
    {
        attr: 'on-disabledAll',
        prop: 'onDisabledAll',
        value: null
    },
    {
        attr: 'on-enabledAll',
        prop: 'onEnabledAll',
        value: null
    },
    {
        attr: 'on-activated',
        prop: 'onActivated',
        value: null
    },
    {
        attr: 'on-updatedItemcont',
        prop: 'onUpdatedItemCont',
        value: null
    },
    {
        attr: 'on-updatedcont',
        prop: 'onUpdatedCont',
        value: null
    },
    {
        attr: 'on-request',
        prop: 'onRequest',
        value: null
    },
    ...optBase
];

class Tab extends ModBaseListenCacheNest {
    options = {};
    actEvt;
    trigger;
    treeDataOrig;
    observeIns;
    headsEl;
    bodiesEl;
    dividerEl;
    rawData;
    headLayout;
    contXhr;
    static hostType = 'node';
    static optMaps = optMenu;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            host: elem,
            maps: Tab.optMaps,
            component: false,
            spread: ['slider', 'col', 'menu', 'tools', 'divider']
        });
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        let _this = this;
        this.actEvt = function (evt) {
            if (this.hasAttribute('selected') || this.hasAttribute('disabled'))
                return;
            let toolsEl = this.querySelector(`:scope > [${alias}="tools"]`);
            if (toolsEl && toolsEl.contains(evt.target))
                return;
            let item = _this.treeData.find((k) => k.headEl === this);
            _this.activate(item);
        };
        this.trigger = this.options.trigger === 'hover' ? 'mouseenter' : 'click';
        this.treeDataOrig = [];
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        super.useTpl();
        await this.getDataToRender();
        this.setAttrs();
        super.initDisableds();
        this.renderFinish();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    initActive() {
        let activeItem = this.treeData.findIndex((k) => k.selected);
        if (this.options.active === 0 || this.options.active) {
            this.activate(this.options.active);
        }
        else if (activeItem < 0) {
            this.activate(0);
        }
    }
    async getDataToRender() {
        let base = {
            nodeType: 'tab',
        }, promise;
        if (this.options.content) {
            promise = treeTools.allToTree({
                content: this.options.content,
                contType: this.options.contType,
                contData: this.options.contData,
                ajax: { spinSel: this.targetEl, xhrName: 'contXhr' },
                ins: this,
                ...base
            });
        }
        else {
            if (this.targetEl.querySelector(`.${prefix}tab-head`) && this.targetEl.querySelector(`.${prefix}tab-body`)) {
                this.treeDataOrig = this.htmlToArr();
                treeTools.addIdPath({ source: this.treeDataOrig });
                this.rawData = this.treeDataOrig;
                this.updateHeadBodyAttrs();
            }
            else {
                promise = treeTools.allToTree({
                    content: this.targetEl.innerHTML,
                    ...base
                });
            }
        }
        if (promise) {
            await promise.then((res) => {
                
                this.treeDataOrig = res.data && Array.isArray(res.data) ? res.data : res;
                this.rawData = res;
            });
            this.targetEl.innerHTML = '';
            this.renderData(this.treeDataOrig);
        }
        this.getTreeFlat();
        super.listen({ name: 'rendered', params: [this.treeData] });
        return this;
    }
    getTreeFlat() {
        this.treeData = this.getObserver(this.treeDataOrig).proxy;
    }
    getObserver(data) {
        this.observeIns ? this.observeIns.destroy() : null;
        this.observeIns = new Observe(data, {
            deep: {
                enable: true,
                exclude: ['tools'],
            },
            onSet: (obj) => {
                if (obj.key === 'icon' && obj.target.iconEl) {
                    obj.target.iconEl.className = obj.value;
                }
                else if (obj.key === 'cube' && obj.target.cubeEl) {
                    obj.target.cubeEl.src = obj.value;
                }
                else if (obj.key === 'disk' && obj.target.diskEl) {
                    obj.target.diskEl.src = obj.value;
                }
                else if (obj.key === 'image' && obj.target.imageEl) {
                    obj.target.imageEl.src = obj.value;
                }
                else if (obj.key === 'label') {
                    obj.target.labelEl.innerHTML = obj.value;
                }
                else if (obj.key === 'tips' && obj.target.tipsEl) {
                    obj.target.tipsEl.innerHTML = obj.value;
                }
                else if (obj.key === 'badge' && obj.target.badgeEl) {
                    obj.target.badgeEl.setAttribute('label', obj.value);
                }
                else if (obj.key === 'selected') {
                    if (obj.value === true) {
                        obj.target.headEl.setAttribute('selected', '');
                        obj.target.bodyEl.setAttribute('selected', '');
                        !obj.target.bodyEl.innerHTML && this.fillContent(obj.target);
                    }
                    else {
                        obj.target.headEl.removeAttribute('selected');
                        obj.target.bodyEl.removeAttribute('selected');
                    }
                }
                else if (obj.key === 'disabled') {
                    obj.target.headEl.toggleAttribute('disabled', obj.value);
                }
                else if (obj.key === 'content') {
                    this.fillContent(obj.target);
                }
            },
            onCompleted: () => {
                let activeItem = this.getSelected(), tmp = {
                    active: activeItem ? (activeItem.hasOwnProperty('id') ? activeItem.id : this.treeData.indexOf(activeItem)) : 0,
                    disabled: super.getDisableds().map((k, i) => k.hasOwnProperty('id') ? k.id : i),
                    content: !isEmpty(this.options.content) ? deepClone(this.treeDataOrig) : [],
                };
                super.updateCache(tmp);
            }
        });
        return this.observeIns;
    }
    htmlToArr() {
        
        let data = [];
        this.headsEl = this.targetEl.querySelector(`.${prefix}tab-head`);
        this.bodiesEl = this.targetEl.querySelector(`.${prefix}tab-body`);
        if (!this.headsEl || !this.bodiesEl) {
            return data;
        }
        let tmpHeads = getEls(this.options.selector.head, this.headsEl), tmpBodys = getEls(this.options.selector.body, this.bodiesEl), headsArr = tmpHeads.length ? tmpHeads : [...this.headsEl.children], bodiesArr = tmpBodys.length ? tmpBodys : [...this.bodiesEl.children];
        if (headsArr.length !== bodiesArr.length) {
            console.warn('Inconsistent number of heads and bodies!');
            return data;
        }
        let setBoolean = (obj, props) => {
            let loop = (prop) => {
                let val = obj.headEl.hasAttribute(prop);
                if (val && val !== 'false' && val !== '0') {
                    obj[prop] = true;
                    obj.headEl.setAttribute(prop, '');
                }
                else {
                    obj[prop] = false;
                }
            };
            if (Array.isArray(props)) {
                props.forEach((k) => {
                    loop(k);
                });
            }
            else {
                loop(props);
            }
        }, getPropsFromHead = (elem) => {
            let obj = {}, tmpLabel = elem.querySelector(`[${alias}="label"]`);
            obj.headEl = elem;
            obj.labelEl = tmpLabel || elem;
            obj.label = obj.labelEl.textContent.trim();
            obj.tipsEl = elem.querySelector(`[${alias}="tips"]`);
            obj.tips = obj.tipsEl ? obj.tipsEl.textContent.trim() : '';
            obj.iconEl = elem.querySelector(`[${alias}="icon"]`);
            obj.icon = obj.iconEl ? obj.iconEl.getAttribute('class') : '';
            obj.cubeEl = elem.querySelector(`[${alias}="cube"]`);
            obj.cube = obj.cubeEl ? obj.cubeEl.src : '';
            obj.diskEl = elem.querySelector(`[${alias}="disk"]`);
            obj.disk = obj.diskEl ? obj.diskEl.src : '';
            obj.badgeEl = elem.querySelector(`[${alias}="badge"],ax-badge`);
            obj.badge = obj.badgeEl ? obj.badgeEl.getAttribute('label') || obj.badgeEl.textContent.trim() : '';
            setBoolean(obj, ['selected', 'disabled']);
            return obj;
        };
        data = headsArr.map((k, i) => {
            let tmp = getPropsFromHead(k), bodyEl = bodiesArr[i];
            bodyEl.toggleAttribute('selected', !!tmp.selected);
            this.options.tools.enable && this.getTools(tmp);
            return { ...tmp, bodyEl, content: bodyEl.innerHTML };
        });
        return data;
    }
    renderData(data) {
        this.dividerEl = createEl('ax-br');
        this.options.divider.size && this.dividerEl.setAttribute('size', this.options.divider.size);
        this.headsEl = createEl('ul', { class: `${prefix}tab-head` });
        this.bodiesEl = createEl('ul', { class: `${prefix}tab-body` });
        this.updateHeadBodyAttrs();
        for (let k of data) {
            if (!k.hasOwnProperty('label') || !k.hasOwnProperty('content')) {
                continue;
            }
            else {
                this.createItemEl(k);
                this.headsEl.appendChild(k.headEl);
                this.bodiesEl.appendChild(k.bodyEl);
            }
        }
        super.listen({ name: 'insertItems', params: [data] });
        this.targetEl.append(this.headsEl, this.options.divider.enable ? this.dividerEl : '', this.bodiesEl);
    }
    createItemEl(item) {
        item.labelEl = createEl('i', { [alias]: 'label' }, item.label);
        item.iconEl = item.icon ? createEl('i', { [alias]: 'icon', class: item.icon }) : null;
        item.diskEl = item.disk ? createEl('img', { [alias]: 'disk', src: item.disk }) : null;
        item.cubeEl = item.cube ? createEl('img', { [alias]: 'cube', src: item.cube }) : null;
        item.imageEl = item.image ? createEl('img', { [alias]: 'image', src: item.image }) : null;
        item.headEl = createEl('li');
        item.bodyEl = createEl('li');
        item.badgeEl = item.badge ? createEl('ax-badge', { [alias]: 'badge' }, item?.badge?.trim()) : null;
        item.tipsEl = item.tips ? createEl('i', { [alias]: 'tips' }, item.tips) : null;
        item.selected && (item.headEl.toggleAttribute('selected', true), item.bodyEl.toggleAttribute('selected', true));
        item.disabled && item.headEl.toggleAttribute('disabled', true);
        item.headEl.append(...[item.iconEl, item.diskEl, item.cubeEl, item.imageEl, item.labelEl, item.tipsEl, item.badgeEl].filter(Boolean));
        this.options.tools.enable && this.getTools(item);
    }
    getTools(item) {
        if (this.options.tools.enable) {
            item.toolsEl = createTools(this.options.tools.children, item.headEl);
            item.toolsEl.setAttribute(alias, 'tools');
            item.tools = deepClone(this.options.tools.children);
            for (let k of item.tools) {
                this.options.lang.title[k.name] && k.wrapEl.setAttribute('title', this.options.lang.title[k.name]);
                if (k.name === 'add') {
                    item.addEl = k.wrapEl;
                }
                else if (k.name === 'edit') {
                    item.editEl = k.wrapEl;
                }
                else if (k.name === 'close') {
                    item.closeEl = k.wrapEl;
                }
                else if (k.name === 'update') {
                    item.updateEl = k.wrapEl;
                }
                else if (k.name === 'move') {
                    item.moveEl = k.wrapEl;
                }
            }
        }
    }
    async fillContent(obj, cb) {
        await getContent.call(this, {
            content: obj.content,
            contType: obj.contType,
            contData: obj.contData,
            ajax: {
                ajaxType: obj.ajaxType || this.options.ajaxType,
                xhrName: 'contXhr',
                spinSel: obj.bodyEl,
            },
            cb: (data) => {
                setContent({
                    content: data,
                    target: obj.bodyEl,
                    engine: obj.engine || this.tplStr,
                    template: obj.template || this.tplEng
                });
                cb && cb(data);
            }
        });
    }
    updateHeadBodyAttrs() {
        this.options.classes?.head && classes(this.headsEl).add(this.options.classes.head);
        this.options.classes?.body && classes(this.bodiesEl).add(this.options.classes.body);
        !isEmpty(this.options.attrs?.head) && setAttrs(this.headsEl, this.options.attrs.head);
        !isEmpty(this.options.attrs?.body) && setAttrs(this.bodiesEl, this.options.attrs.body);
        if (this.options.layout) {
            if (!this.headsEl.getAttribute('class')?.includes(`${prefix}group-`)) {
                this.headLayout = `${prefix}group-${this.options.layout}`;
                this.headsEl.classList.add(this.headLayout);
            }
        }
        else {
            this.headLayout && this.headsEl.classList.remove(this.headLayout);
        }
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}tab`);
        this.options.classes?.target && classes(this.targetEl).add(this.options.classes.target);
        !isEmpty(this.options.attrs?.target) && setAttrs(this.targetEl, this.options.attrs.target);
        this.targetEl.toggleAttribute('inert', this.options.passive);
        this.targetEl.toggleAttribute('embed', this.options.embed);
        this.options.col.enable ? this.targetEl.setAttribute('col', this.options.col.position) : this.targetEl.removeAttribute('col');
    }
    updateEvt(k) {
        this.addTrigger(k);
        if (k.toolsEl) {
            if (k.closeEl) {
                k.closeEl.onclick = () => {
                    if (k.readonly)
                        return;
                    this.remove(k);
                };
            }
            if (k.editEl) {
                k.editEl.onclick = debounce(() => {
                });
            }
            if (k.addEl) {
                k.addEl.onclick = debounce(() => {
                    if (k.disabled)
                        return;
                    this.add({ data: {}, isFront: false, target: k });
                });
            }
            if (k.updateEl) {
                k.updateEl.onclick = debounce(() => {
                    if (k.disabled)
                        return;
                    this.updateItemCont({ content: k.content, target: k });
                });
            }
            for (let [i, o] of this.options.tools.children.entries()) {
                let tool = k.tools[i], refer = { ins: this, item: k };
                o.action && o.action.call(refer, tool);
                tool.action && tool.action.call(refer, tool);
            }
        }
        k.disabled && k.headEl.toggleAttribute('disabled', true);
        k.action && k.action.call(this, k);
    }
    renderFinish() {
        this.initActive();
        for (let k of this.treeData) {
            this.updateEvt(k);
        }
    }
    getSelected() {
        return this.treeData.find((k) => k.selected);
    }
    addTrigger(item) {
        if (!item.disabled) {
            item.headEl.removeEventListener(this.trigger, this.actEvt);
            item.headEl.addEventListener(this.trigger, this.actEvt, false);
        }
    }
    removeTrigger(item) {
        if (item.disabled) {
            item.headEl.removeEventListener(this.trigger, this.actEvt);
        }
    }
    toggleDisabled(item, type = 'disable') {
        if (type === 'enable') {
            item.disabled = false;
            this.addTrigger(item);
        }
        else if (type === 'disable') {
            item.disabled = true;
            this.removeTrigger(item);
        }
    }
    disable(data, cb) {
        if (this.destroyed || (data !== 0 && isEmpty(data))) {
            return this;
        }
        let tmp = Array.isArray(data) ? data : [data], arr = tmp.map((k) => findItem(k, this.treeData)).filter(Boolean), fun = (k) => this.toggleDisabled(k, 'disable');
        arr.forEach(k => fun(k));
        super.listen({ name: 'disabled', cb, params: [arr] });
        return this;
    }
    disableAll(cb) {
        if (this.destroyed) {
            return this;
        }
        this.disable(this.treeData);
        super.listen({ name: 'disabledAll', cb });
        return this;
    }
    enable(data, cb) {
        if (this.destroyed || (data !== 0 && isEmpty(data))) {
            return this;
        }
        let tmp = Array.isArray(data) ? data : [data], arr = tmp.map((k) => findItem(k, this.treeData)).filter(Boolean), fun = (k) => this.toggleDisabled(k, 'enable');
        arr.forEach(k => fun(k));
        super.listen({ name: 'enabled', cb, params: [arr] });
        return this;
    }
    enableAll(cb) {
        if (this.destroyed) {
            return this;
        }
        this.enable(this.treeData);
        super.listen({ name: 'enabledAll', cb });
        return this;
    }
    async activate(data, cb) {
        if (this.destroyed || isNull(data))
            return;
        let item = findItem(data, this.treeData), other = this.treeData.find((k) => k !== item && k.selected);
        if (!item)
            return;
        if (item.selected) {
            !item.bodyEl.innerHTML && this.fillContent(item);
        }
        else {
            try {
                if (this.options.b4Activate) {
                    let resp = await this.options.b4Add.call(this, item);
                    resp && (item = resp);
                }
            }
            catch (err) {
                err ? console.error(err) : console.warn(`Activating branche (${item.label}) has been prevented!`);
            }
            other && (other.selected = false);
            item.selected = true;
            item.disabled = false;
            super.listen({ name: 'activated', cb, params: [item] });
        }
        return this;
    }
    async add(options) {
        if (this.destroyed)
            return this;
        if (this.treeData.length >= this.options.max && this.options.max > 0) {
            throw new Error('The number of children exceeds the maximum!');
        }
        let opts = Object.assign({ repeat: true, autoFill: true, isFront: false }, options), type = getDataType(opts.data), targetObj = findItem(opts.target, this.treeData), items = type === 'Array' ? opts.data : type === 'Object' ? [opts.data] : [{}];
        if (!opts.repeat) {
            let labels = this.treeData.map((k) => k.label);
            items = items.filter((k) => !labels.includes(k.label));
        }
        if (items.length === 0) {
            return this;
        }
        for (let k of items) {
            try {
                if (this.options.b4Add) {
                    let resp = await this.options.b4Add.call(this, k);
                    resp && (k = resp);
                }
            }
            catch (err) {
                err ? console.error(err) : console.warn(`Adding new branche (${JSON.stringify(k)}) has been prevented!`);
                continue;
            }
            !k.hasOwnProperty('id') && (k.id = increaseId(this.treeData));
            !k.hasOwnProperty('label') && (k.label = `${this.options.lang.label}${k.id}`);
            !k.hasOwnProperty('content') && (k.content = `${this.options.lang.content}${k.id}`);
            this.createItemEl(k);
        }
        if (targetObj) {
            let tmp = this.treeData.indexOf(targetObj), index = opts.isFront ? tmp : tmp + 1;
            splice({
                host: this.treeData,
                intent: 'add',
                source: items,
                index,
            });
        }
        else {
            splice({
                host: this.treeData,
                intent: opts.isFront ? 'start+' : 'end+',
                source: items,
            });
        }
        let heads = items.map((k) => k.headEl), bodies = items.map((k) => k.bodyEl);
        appendEls({ parent: this.headsEl, nodes: heads, target: targetObj?.headEl, prepend: opts.isFront ? true : false });
        appendEls({ parent: this.bodiesEl, nodes: bodies, target: targetObj?.bodyEl, prepend: opts.isFront ? true : false });
        this.options.addActive && this.activate(items[items.length - 1]);
        let tmp = items.map((k) => {
            let _tmp = this.treeData.find((i) => (i.id === k.id));
            this.updateEvt(_tmp);
            return _tmp;
        });
        super.listen({ name: 'added', cb: opts.cb, params: [tmp] });
        return this;
    }
    async remove(data, cb) {
        if (this.destroyed || isNull(data)) {
            return this;
        }
        if (this.treeData.length === 0) {
            console.warn('The source data is already empty!');
            return this;
        }
        let tmp = Array.isArray(data) ? data : [data], items = tmp.map((k) => findItem(k, this.treeData)).filter(Boolean), removeItem = (obj) => {
            let index = this.treeData.indexOf(obj);
            this.treeData.splice(index, 1);
            obj.headEl.remove();
            obj.bodyEl.remove();
        };
        for (let k of items) {
            try {
                if (this.options.b4Remove) {
                    let resp = await this.options.b4Remove.call(this, k);
                    resp && (k = resp);
                }
            }
            catch (err) {
                err ? console.error(err) : console.warn(`Removing old branch (${JSON.stringify(k)}) has been prevented!`);
                continue;
            }
            removeItem(k);
        }
        super.listen({ name: 'removed', cb, params: [items] });
        return this;
    }
    async edit({ source, target, cb }) {
        if (this.destroyed || isEmpty(source) || isNull(target)) {
            return this;
        }
        let item = findItem(target, this.treeData);
        if (!item)
            return this;
        for (let k in source) {
            let val = source[k];
            try {
                if (this.options.b4Edit) {
                    let resp = await this.options.b4Edit.call(this, { key: k, value: val }, item);
                    resp && (val = resp);
                }
                if (k === 'action') {
                    item[k] = val.call({ ins: this, item: k }, item);
                }
                else if (['icon', 'disk', 'cube', 'image', 'label', 'tips', 'badge', 'disabled', 'content', 'contType', 'contData', 'ajaxType'].includes(k)) {
                    item[k] = val;
                }
                else if (k === 'selected') {
                    val === true ? this.activate(item) : item[k] = val;
                }
            }
            catch (err) {
                err ? console.error(err) : console.warn(`No changes have been made to the ${k} property!`);
                return this;
            }
        }
        super.listen({ name: 'edited', cb, params: [item] });
        return this;
    }
    async graft({ source, target, isFront = true, cb }) {
        if (this.destroyed || isNull(source)) {
            return this;
        }
        let sourceObj = findItem(source, this.treeData), targetObj = findItem(target, this.treeData);
        if (!sourceObj)
            return this;
        try {
            if (this.options.b4Graft) {
                let resp = await this.options.b4Graft.call(this, sourceObj, targetObj);
                resp && resp.source && (sourceObj = resp.source);
                resp && resp.target && (targetObj = resp.target);
            }
        }
        catch (err) {
            err ? console.error(err) : console.warn(`The branch relationship between source and target in the tree remains unchanged!`);
            return this;
        }
        let position = '';
        if (targetObj) {
            position = isFront ? 'beforeBegin' : 'afterEnd';
            targetObj.headEl.insertAdjacentElement(position, sourceObj.headEl);
            targetObj.bodyEl.insertAdjacentElement(position, sourceObj.bodyEl);
        }
        else {
            position = isFront ? 'afterBegin' : 'beforeEnd';
            this.headsEl.insertAdjacentElement(position, sourceObj.headEl);
            this.bodiesEl.insertAdjacentElement('beforeEnd', sourceObj.bodyEl);
        }
        moveItem({ source: sourceObj, target: targetObj, data: this.treeData, isFront });
        super.listen({ name: 'grafted', cb, params: [sourceObj, targetObj] });
        return this;
    }
    updateItemCont({ target, content, contType, contData, ajaxType, cb }) {
        if (this.destroyed)
            return this;
        let targetObj = findItem(target, this.treeData);
        if (isEmpty(content) || !targetObj || content == targetObj.content)
            return this;
        contType ? (targetObj.contType = contType) : this.options.contType !== 'text' ? (targetObj.contType = this.options.contType) : null;
        contData && (targetObj.contData = contData);
        ajaxType && (targetObj.ajaxType = ajaxType);
        targetObj.content = content;
        super.listen({ name: 'updatedItemCont', cb, params: [targetObj] });
        return this;
    }
    
    async updateCont(content, cb) {
        if (this.destroyed) {
            return this;
        }
        await getContent.call(this, {
            content,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: {
                ajaxType: this.options.ajaxType,
                xhrName: 'contXhr',
            },
            cb: (data) => {
                if (Array.isArray(data)) {
                    this.treeDataOrig = deepClone(data);
                    treeTools.addIdPath({ source: this.treeDataOrig });
                    this.targetEl.innerHTML = '';
                    this.renderData(this.treeDataOrig);
                    this.getTreeFlat();
                    this.renderFinish();
                    super.updateCache({ content });
                    super.listen({ name: 'updatedCont', cb, params: [this.treeDataOrig] });
                }
            }
        });
        return this;
    }
    
    destroy(cb) {
        if (this.destroyed) {
            return this;
        }
        this.treeData.forEach((k) => {
            this.removeTrigger(k);
        });
        this.contXhr && this.contXhr.abort();
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

class Tooltip extends Popup {
    constructor(elem, options = {}, initial = config.initial) {
        isNull(options.size) ? options.size = 'sm' : null;
        options?.tools === true ? options.tools = { enable: true, placement: 'outside' } : !options?.tools ? options.tools = { enable: false, placement: 'outside' } : null;
        !options?.placement ? options.placement = 'top' : null;
        !options?.footer ? options.footer = false : null;
        !options?.padding ? options.padding = false : null;
        !options?.theme ? options.theme = 'text' : null;
        super(elem, options, initial);
        this.on('initiated', () => {
            this.mainEl.classList.add(`${prefix}tooltip`);
        });
    }
}

var Tooltip$1 = /*#__PURE__*/Object.freeze({
    __proto__: null,
    default: Tooltip
});

const optDropdown = [
    {
        attr: 'content',
        prop: 'content',
        value: '',
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'placement',
        prop: 'placement',
        value: 'bottom-start',
    },
    {
        attr: 'list-type',
        prop: 'listType',
        value: '',
    },
    {
        attr: 'unpadded',
        prop: 'unpadded',
        value: false,
    },
    {
        attr: 'hoverable',
        prop: 'hoverable',
        value: false,
    },
    {
        attr: 'divisible',
        prop: 'divisible',
        value: false,
    },
    {
        attr: 'multiline',
        prop: 'multiline',
        value: false,
    },
    {
        attr: 'cols',
        prop: 'cols',
        value: 0,
    },
    {
        attr: 'lines',
        prop: 'lines',
        value: '',
    },
    {
        attr: 'detectable',
        prop: 'detectable',
        value: false,
    },
    {
        attr: 'child-sel',
        prop: 'childSel',
        value: `[${alias}="label"]`,
    },
    {
        attr: 'val-key',
        prop: 'valKey',
        value: '',
    },
    {
        attr: 'feature',
        prop: 'feature',
        value: '',
    },
    {
        attr: 'popup',
        prop: 'popup',
        value: {},
    },
    ...optBase
];

class Dropdown extends ModBaseListenCache {
    popupIns;
    static hostType = 'node';
    static optMaps = optDropdown;
    constructor(elem, options = {}, initial = config.initial) {
        super();
        super.ready({
            options,
            host: elem,
            maps: Dropdown.optMaps,
            component: true,
            spread: []
        });
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.popupIns && this.popupIns.destroy();
        let _this = this, dftParams = {
            content: this.options.content,
            contType: this.options.contType,
            contData: this.options.contData,
            tools: false,
            trigger: 'click',
            placement: 'bottom-start',
            classes: `${prefix}dropdown`,
            autoFill: {
                enable: true,
                childSel: this.options.childSel,
                detectable: this.options.detectable,
                key: this.options.valKey,
            },
            bullet: {
                enable: !!this.options.listType,
                type: this.options.listType,
                unpadded: this.options.unpadded,
                hoverable: this.options.hoverable,
                divisible: this.options.divisible,
                multiline: this.options.multiline,
                cols: this.options.cols,
                lines: this.options.lines,
                action: this.options.action ? this.options.action.bind(this) : null,
            },
            padding: this.options.divisible || this.options.lines === 'fence' ? false : true,
            onInitiated: function () {
                _this.options.feature && this.mainEl.setAttribute('feature', _this.options.feature);
            }
        }, params = extend({
            target: dftParams,
            source: this.options.popup,
        });
        this.setFeature(params);
        this.popupIns = new Popup(this.targetEl, params);
        this.setAttrs();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    setAttrs() {
    }
    setFeature(params) {
        if (this.options.feature === 'button') {
            params.footer = params.padding = params.autoFill.enable = false;
            params.bullet.enable = params.bullet.divisible = true;
            params.bullet.type = params.bullet.type || 'custom';
            params.placement = 'bottom-center';
            params.size = '';
        }
        else if (this.options.feature === 'tooltip') {
            params.footer = params.bullet.enable = false;
            params.placement = 'top-center';
            params.size = '';
        }
        else if (this.options.feature === 'select') {
            params.footer = params.padding = false;
            params.bullet.enable = params.autoFill.enable = params.bullet.divisible = true;
            params.bullet.type = 'select-single';
            params.placement = 'bottom-center';
            params.size = 'sm';
        }
    }
    destroy() {
        if (this.destroyed)
            return this;
        this.popupIns.destroy();
        return this;
    }
}

let AXTMP_hyphen$3 = config.splitHyphen;
const optRetrieval = [
    {
        attr: 'keys',
        prop: 'keys',
        value: '',
    },
    {
        attr: 'props',
        prop: 'props',
        value: 'label',
    },
    {
        attr: 'fuzzy',
        prop: 'fuzzy',
        value: true,
    },
    {
        attr: 'order',
        prop: 'order',
        value: '',
    },
    {
        attr: 'limit',
        prop: 'limit',
        value: 0,
    },
    {
        attr: 'ignore',
        prop: 'ignore',
        value: true,
    },
    {
        attr: 'hyphen',
        prop: 'hyphen',
        value: AXTMP_hyphen$3,
    },
    {
        attr: 'null-handle',
        prop: 'nullHandle',
        value: 'reset',
    },
    {
        attr: 'highlight',
        prop: 'highlight',
        value: {
            enable: false,
            props: 'label',
            node: 'i',
            classes: `${prefix}c-issue`,
        },
    },
    {
        attr: 'content',
        prop: 'content',
        value: '',
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'target',
        prop: 'target',
        value: '',
    },
    {
        attr: 'editor',
        prop: 'editor',
        value: '',
    },
    {
        attr: 'trigger',
        prop: 'trigger',
        value: 'input',
    },
    {
        attr: 'delay',
        prop: 'delay',
        value: 200,
    },
    {
        attr: 'status',
        prop: 'status',
        value: {
            enable: false,
            placement: 'list-top',
        },
    },
    {
        attr: 'tpl-str',
        prop: 'tplStr',
        value: '',
    },
    {
        attr: 'tpl-eng',
        prop: 'tplEng',
        value: null,
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'bullet',
        prop: 'bullet',
        value: {
            type: 'custom',
            childNode: 'div',
            unpadded: false,
            hoverable: false,
            divisible: false,
            multiline: false,
        },
    },
    {
        attr: 'b4-fill',
        prop: 'b4Fill',
        value: null,
    },
    {
        attr: 'on-rendered',
        prop: 'onRendered',
        value: null,
    },
    {
        attr: 'on-searched',
        prop: 'onSearched',
        value: null
    },
    {
        attr: 'on-processed"',
        prop: 'onProcessed"',
        value: null
    },
    {
        attr: 'on-updatedkeys',
        prop: 'onUpdatedKeys',
        value: null
    },
    {
        attr: 'on-request',
        prop: 'onRequest',
        value: null
    },
    {
        attr: 'on-updatedcont',
        prop: 'onUpdatedCont',
        value: null
    },
    ...optBase
];

class Retrieval extends ModBaseListenCache {
    options = {};
    mainEl;
    inputEl;
    statusEl;
    inputEvt;
    keys;
    data;
    output;
    contXhr;
    editorEl;
    static hostType = 'none';
    static optMaps = optRetrieval;
    constructor(options = {}, initial = true) {
        super();
        super.ready({
            options,
            type: Retrieval.hostType,
            maps: Retrieval.optMaps,
            component: false,
            spread: ['status', 'highlight']
        });
        
        
        
        
        
        
        
        
        
        
        
        
        this.targetEl = getEl(this.options.target);
        this.inputEl = getEl(this.options.editor);
        this.statusEl = createEl('div', { class: `${prefix}retrieval-status` });
        this.inputEvt = debounce(() => {
            let keys = this.getInputVals();
            this.updateKeys(keys);
        }, this.options.delay);
        
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.setEmpty();
        this.keys = this.options.keys || this.getInputVals();
        this.useTpl();
        await getContent.call(this, {
            content: this.options.content,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: {
                xhrName: 'contXhr',
                spinSel: this.targetEl,
                ...this.options.ajax
            },
            cb: (data) => {
                if (isEmpty(data))
                    return this;
                this.data = this.getStdData(data);
                super.listen({ name: 'processed', params: [this.data] });
                this.searchData();
                this.render();
            }
        });
        if (elState(this.statusEl).isVirtual && this.options.status.enable) {
            if (this.options.status.placement === 'list-top') {
                this.targetEl.insertAdjacentElement('beforebegin', this.statusEl);
            }
            else if (this.options.status.placement === 'list-btm') {
                this.targetEl.insertAdjacentElement('afterend', this.statusEl);
            }
            else if (this.options.status.placement === 'editor-top') {
                this.editorEl && this.editorEl.insertAdjacentElement('beforebegin', this.statusEl);
            }
            else if (this.options.status.placement === 'editor-btm') {
                this.editorEl && this.editorEl.insertAdjacentElement('afterend', this.statusEl);
            }
            else {
                let target = getEl(this.options.status.placement);
                target && target.appendChild(this.statusEl);
            }
        }
        this.input2Render();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    setEmpty() {
        this.targetEl && (this.targetEl.innerHTML = '');
    }
    setAttrs() {
        if (this.targetEl) {
            this.targetEl.classList.add(`${prefix}retrieval`);
            this.options.classes && classes(this.targetEl).add(this.options.classes);
        }
    }
    getStdData(data) {
        let dataType = getDataType(data), result = [];
        if (dataType === 'String') {
            let tmp = valToArr(data, this.options.hyphen, false);
            result = tmp.filter(Boolean).map((k) => {
                let obj = { label: k, value: k };
                return obj;
            });
        }
        else if (dataType === 'Array') {
            if (data.length === 0)
                return [];
            let childType = getDataType(data[0]);
            if (['String', 'Number'].includes(childType)) {
                result = data.filter(Boolean).map((k) => {
                    let obj = { label: k + '', value: k + '' };
                    return obj;
                });
            }
            else if (childType === 'Object') {
                if (data[0].hasOwnProperty('label')) {
                    result = data.filter(Boolean).map((k) => {
                        !k.hasOwnProperty('value') && (k.value = k.label);
                        return k;
                    });
                }
                else {
                    console.error(`The object array must contain the "label" property.`);
                    result = [];
                }
            }
            else {
                console.error(`The items in the object array must be either strings or objects containing the "label" property.`);
                result = [];
            }
        }
        else {
            console.error(`The data type received is incorrect; a string, string array, or object array is required.`);
            result = [];
        }
        return result;
    }
    searchData(keys = this.keys, data = this.data) {
        if (this.destroyed)
            return this;
        if (isEmpty(keys) && this.options.nullHandle === 'reset') {
            this.output = data.map((k) => {
                return { source: k, weight: 0, keys: [] };
            });
        }
        else {
            this.output = arrSearch({
                keys,
                props: this.options.props,
                source: data,
                hyphen: this.options.hyphen,
                fuzzy: this.options.fuzzy,
                ignore: this.options.ignore,
                order: this.options.order,
                limit: this.options.limit
            });
        }
        for (let k of this.output) {
            k.source.el = this.createItemEl(k);
            k.source.action && k.source.action.call(this, k);
        }
        this.updateStatus();
        super.listen({ name: 'searched', params: [this.output] });
    }
    useTpl() {
        let tplEl = getEl(this.options.tplStr);
        this.tplEng = this.options.tplEng || renderTpl;
        this.tplStr = tplEl?.innerHTML || this.options.tplStr || bulletTools.getIBulletTpl(this.options.bullet);
    }
    createItemEl(item) {
        let tplData = this.options.highlight.enable ? this.getHighlightData(item) : item.source, html = super.getTplcont(tplData);
        return tplToEl(html);
    }
    getHighlightData(data) {
        if (!this.options.highlight.enable)
            return data;
        let props = getIntArr([valToArr(this.options.highlight.props, this.options.hyphen, false), valToArr(this.options.props, this.options.hyphen, false)]), copy = { ...data.source }, keys = data.keys.map((k) => clearRegx(k)).filter(Boolean);
        for (let k in copy) {
            if (props.includes(k) && keys.length !== 0) {
                copy[k] = super.replaceMult(copy[k], keys, { ignore: this.options.ignore, nodename: this.options.highlight.node, classes: this.options.highlight.classes });
                
            }
        }
        return copy;
    }
    async render(data = this.output) {
        if (this.destroyed)
            return this;
        isEmpty(data) && console.warn('There is no content to render, but no errors occurred!');
        if (this.options.b4Fill) {
            let resp = await this.options.b4Fill.call(this, data, this.targetEl);
            resp && (data = resp);
        }
        if (this.targetEl) {
            this.targetEl.innerHTML = '';
            data.length > 0 && this.targetEl.append(...data.map((k) => k.source.el));
            super.listen({ name: 'rendered', params: [data, this.targetEl] });
        }
        this.renderCount++;
    }
    updateKeys(keys = this.getInputVals()) {
        if (this.destroyed || keys.toString() == this.keys.toString())
            return this;
        this.keys = keys;
        this.searchData();
        this.render();
        super.listen({ name: 'updatedKeys', params: [keys] });
    }
    input2Render() {
        if (!this.inputEl || this.destroyed)
            return this;
        this.inputEl.removeEventListener(this.options.trigger, this.inputEvt);
        this.inputEl.addEventListener(this.options.trigger, this.inputEvt, false);
    }
    getInputVals() {
        if (['radio', 'checkbox', 'radio-comp', 'checkbox-comp'].includes(this.inputEl.type)) {
            return this.inputEl.checked ? this.inputEl.value : '';
        }
        else {
            return this.inputEl?.value || '';
        }
    }
    resetList() {
        if (this.destroyed)
            return this;
        this.output = this.data.map((k) => {
            return { source: k, weight: 0, keys: [] };
        });
        for (let k of this.output) {
            k.source.el = this.createItemEl(k);
            k.source.action && k.source.action.call(this, k);
        }
        this.render();
    }
    clearList() {
        if (this.destroyed)
            return this;
        if (this.inputEl) {
            
            this.inputEl?.clear ? this.inputEl.clear() : this.inputEl.value = '';
        }
        this.keys = '';
        this.output = [];
        this.render();
    }
    updateStatus() {
        let data = { value: this.output.length, keys: this.keys };
        this.statusEl.innerHTML = isEmpty(data.keys) ? this.options.lang.nullKeys : renderTpl(this.options.lang.status, data);
    }
    async updateCont(content, cb) {
        if (this.destroyed)
            return this;
        await getContent.call(this, {
            content,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: {
                xhrName: 'contXhr',
                spinSel: this.targetEl,
                ...this.options.ajax
            },
            request: (data) => {
                this.listen({ name: 'request', params: [data] });
            },
            cb: (data) => {
                if (isEmpty(data))
                    return this;
                this.data = this.getStdData(data);
                super.listen({ name: 'processed', params: [this.data] });
                this.searchData();
                this.render();
                this.updateCache({ content });
                this.listen({ name: 'updatedCont', cb, params: [data] });
            }
        });
        return this;
    }
    destroy(cb) {
        if (this.inputEl) {
            this.inputEl.removeEventListener(this.options.trigger, this.inputEvt);
        }
        this.contXhr && this.contXhr.abort();
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

const optAutocomplete = [
    {
        attr: 'field',
        prop: 'field',
        value: 'value',
    },
    {
        attr: 'content',
        prop: 'content',
        value: '',
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'tpl-str',
        prop: 'tplStr',
        value: '',
    },
    {
        attr: 'tpl-eng',
        prop: 'tplEng',
        value: null,
    },
    {
        attr: 'popup',
        prop: 'popup',
        value: {},
    },
    {
        attr: 'retrieval',
        prop: 'retrieval',
        value: {},
    },
    {
        attr: 'b4-fill',
        prop: 'b4Fill',
        value: null,
    },
    {
        attr: 'on-shown',
        prop: 'onShown',
        value: null,
    },
    {
        attr: 'on-hidden',
        prop: 'onHidden',
        value: null,
    },
    {
        attr: 'on-cleared',
        prop: 'onCleared',
        value: null,
    },
    {
        attr: 'on-reset2show',
        prop: 'onReset2Show',
        value: null,
    },
    {
        attr: 'on-updatedcont',
        prop: 'onUpdatedCont',
        value: null,
    },
    ...optBase
];

class Autocomplete extends ModBaseListenCache {
    options = {};
    mainEl;
    rendered;
    popupIns;
    retrievalIns;
    contXhr;
    static hostType = 'node';
    static optMaps = optAutocomplete;
    constructor(elem, options = {}, initial = config.initial) {
        super();
        super.ready({
            options,
            host: elem,
            maps: Autocomplete.optMaps,
            component: false,
            spread: []
        });
        this.rendered = false;
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.setEmpty();
        if (!this.targetEl)
            return this;
        this.createPopup();
        this.createRetrieval();
        this.setAttrs();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    setVals(val) {
        let nodeName = this.targetEl.nodeName;
        if (['AX-INPUT', 'AX-TEXTAREA'].includes(nodeName)) {
            this.targetEl.setAttribute('value', val);
        }
        else if (['INPUT', 'TEXTAREA'].includes(nodeName)) {
            this.targetEl.value = val;
        }
    }
    setEmpty() {
        this.popupIns && this.popupIns.destroy();
        this.popupIns?.contEl && (this.popupIns.contEl.innerHTML = '');
        this.retrievalIns && this.retrievalIns.destroy();
    }
    setAttrs() {
        this.popupIns.mainEl.classList.add(`${prefix}autocomplete`);
    }
    createPopup() {
        this.popupIns = new Popup(this.targetEl, extend({
            target: {
                placement: 'bottom-start',
                tools: { enable: true, placement: 'outside' },
                pageClose: false,
                footer: false,
                trigger: 'input',
                content: '',
                padding: false,
                b4Show: () => {
                    return new Promise((resolve) => {
                        if (this.rendered) {
                            resolve();
                        }
                        else {
                            this.popupIns.state = 'hidden';
                        }
                    });
                },
                onHidden: () => {
                    this.rendered = false;
                }
            },
            source: this.options.popup
        }));
    }
    createRetrieval() {
        this.retrievalIns = new Retrieval(extend({
            target: {
                target: this.popupIns.contEl,
                editor: this.targetEl,
                content: this.options.content,
                contType: this.options.contType,
                contData: this.options.contData,
                ajax: this.options.ajax,
                tplStr: this.options.tplStr,
                tplEng: this.options.tplEng,
                status: true,
                highlight: true,
                nullHandle: 'clear',
                bubble: {
                    divisible: true,
                    hoverable: true,
                },
            },
            source: this.options.retrieval
        }));
        this.retrievalIns.on('rendered', (data) => {
            for (let k of data) {
                k.source.el.onclick = async () => {
                    let val = k.source[this.options.field];
                    if (this.options.b4Fill) {
                        let resp = await this.options.b4Fill.call(this, k, this.targetEl);
                        resp && (val = resp);
                    }
                    this.setVals(val);
                    this.hide();
                };
            }
            if (this.retrievalIns.renderCount > 0) {
                this.rendered = true;
                this.retrievalIns.getInputVals().trim() && this.popupIns.show();
                this.rendered = false;
            }
        });
    }
    reset2Show(cb) {
        if (this.destroyed)
            return this;
        this.retrievalIns.resetList();
        this.show();
        super.listen({ name: 'reset2Show', cb });
        return this;
    }
    clear(cb) {
        if (this.destroyed)
            return this;
        this.retrievalIns.clearList();
        this.hide();
        super.listen({ name: 'cleared', cb });
        return this;
    }
    show(cb) {
        if (this.destroyed)
            return this;
        this.rendered = true;
        this.popupIns.show();
        super.listen({ name: 'shown', cb });
        return this;
    }
    hide(cb) {
        if (this.destroyed)
            return this;
        this.popupIns.hide();
        super.listen({ name: 'hidden', cb });
        return this;
    }
    updateCont(content, cb) {
        if (this.destroyed)
            return this;
        this.retrievalIns.updateCont(content, (data) => {
            this.updateCache({ content });
            this.listen({ name: 'updatedCont', cb, params: [data] });
        });
        return this;
    }
    destroy(cb) {
        this.retrievalIns.destroy();
        this.popupIns.destroy();
        this.contXhr && this.contXhr.abort();
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

const optScroll = [
    {
        attr: 'wrapper',
        prop: 'wrapper',
        value: '',
    },
    {
        attr: 'axis',
        prop: 'axis',
        value: 'y',
    },
    {
        attr: 'bounce',
        prop: 'bounce',
        value: true,
    },
    {
        attr: 'delay',
        prop: 'delay',
        value: 200,
    },
    {
        attr: 'gridded',
        prop: 'gridded',
        value: false,
    },
    {
        attr: 'snap',
        prop: 'snap',
        value: {
            enable: false,
            mode: 'auto',
            force: false,
            offset: 0,
            selector: '',
        },
    },
    {
        attr: 'drift',
        prop: 'drift',
        value: {
            enable: false,
            mode: 'auto',
            value: 0,
            coef: 2,
        },
    },
    {
        attr: 'swipe',
        prop: 'swipe',
        value: {
            enable: false,
            mode: 'auto',
            value: 0,
            coef: 2,
        },
    },
    {
        attr: 'bar',
        prop: 'bar',
        value: {
            enable: false,
            sticky: false,
            triggerable: true,
        },
    },
    {
        attr: 'dmp-ratio',
        prop: 'dmpRatio',
        value: 0.5,
    },
    {
        attr: 'initial',
        prop: 'initial',
        value: {
            value: 0,
            mode: 'scroll',
        },
    },
    {
        attr: 'child',
        prop: 'child',
        value: {
            size: null,
            gap: 0,
            settable: false,
        },
    },
    {
        attr: 'toinit-mode',
        prop: 'toInitMode',
        value: 'scroll',
    },
    {
        attr: 'duration',
        prop: 'duration',
        value: 300,
    },
    {
        attr: 'resize-thr',
        prop: 'resizeThr',
        value: 1,
    },
    {
        attr: 'wheel',
        prop: 'wheel',
        value: {
            mode: 'const',
            value: 0,
            reverse: false,
        },
    },
    {
        attr: 'keyboard',
        prop: 'keyboard',
        value: false,
    },
    {
        attr: 'completed-thr',
        prop: 'completedThr',
        value: 50,
    },
    {
        attr: 'paddingStart',
        prop: 'paddingStart',
        value: 0,
    },
    {
        attr: 'padding-end',
        prop: 'paddingEnd',
        value: 0,
    },
    {
        attr: 'insertable',
        prop: 'insertable',
        value: true,
    },
    {
        attr: 'gesture',
        prop: 'gesture',
        value: {},
    },
    {
        attr: 'b4-tostart',
        prop: 'b4ToStart',
        value: null,
    },
    {
        attr: 'b4-toend',
        prop: 'b4ToEnd',
        value: null,
    },
    {
        attr: 'on-completed',
        prop: 'onCompleted',
        value: null,
    },
    {
        attr: 'on-exceeded',
        prop: 'onExceed',
        value: null,
    },
    {
        attr: 'on-start',
        prop: 'onStart',
        value: null,
    },
    {
        attr: 'on-move',
        prop: 'onMove',
        value: null,
    },
    {
        attr: 'on-end',
        prop: 'onEnd',
        value: null,
    },
    {
        attr: 'on-getsnappeds',
        prop: 'onGetSnappeds',
        value: null,
    },
    {
        attr: 'on-refactored',
        prop: 'onRefactored',
        value: null,
    },
    {
        attr: 'on-snap',
        prop: 'onSnap',
        value: null,
    },
    {
        attr: 'on-snapped',
        prop: 'onSnapped',
        value: null,
    },
    {
        attr: 'on-resized',
        prop: 'onResized',
        value: null,
    },
    {
        attr: 'on-scroll',
        prop: 'onScroll',
        value: null,
    },
    {
        attr: 'on-scrolled',
        prop: 'onScrolled',
        value: null,
    },
    {
        attr: 'on-locate',
        prop: 'onLocate',
        value: null,
    },
    {
        attr: 'on-located',
        prop: 'onLocated',
        value: null,
    },
    ...optBase
];

class Scroll extends ModBaseListenCache {
    options = {};
    forwardMap;
    reverseMap;
    wrapEl;
    snappeds;
    unsnappeds;
    snapped;
    started;
    scrolled;
    cross;
    isMobi;
    childAvgSize;
    resizeCount;
    mutateCount;
    canResize;
    canMutate;
    initialResize;
    key2Trans;
    resizeListener;
    mutationListener;
    targetSize;
    reverseSize;
    wrapSize;
    paddingStart;
    paddingEnd;
    rangeEnd;
    rangeStart;
    activeSize;
    baseSize;
    isBaby;
    transNow;
    transLast;
    gap;
    barSize;
    progress;
    trackEl;
    thumbEl;
    barIns;
    gestureIns;
    wrapStyleObs;
    sizeObs;
    static hostType = 'node';
    static optMaps = optScroll;
    constructor(elem, options = {}, initial = config.initial) {
        super();
        super.ready({
            options,
            host: elem,
            maps: Scroll.optMaps,
            component: false,
            spread: ['snap', 'drift', 'swipe', 'bar', 'keyboard']
        });
        this.forwardMap = propsMap[this.options.axis];
        this.reverseMap = propsMap[this.options.axis === 'x' ? 'y' : 'x'];
        this.wrapEl = this.options.wrapper ? getEl(this.options.wrapper) : this.targetEl.firstElementChild;
        if (!this.wrapEl)
            return this;
        this.snappeds = [];
        this.unsnappeds = [];
        this.snapped = null;
        this.started = false;
        this.scrolled = true;
        this.cross = '';
        this.isMobi = isMobi;
        this.childAvgSize = null;
        this.resizeCount = 0;
        this.mutateCount = 0;
        this.canResize = true;
        this.canMutate = true;
        this.initialResize = false;
        this.key2Trans = throttle((e) => {
            (e.code === 'ArrowLeft') ? this.toPrev() : (e.code === 'ArrowRight') ? this.toNext() : null;
        }, { intvl: this.options.keyboard.intvl, prevent: true });
        this.resizeListener = debounce((data) => {
            this.resizeCount++;
            for (let k of data) {
                if (!k.contentRect.width && !k.contentRect.height)
                    return;
                this.response();
            }
            super.listen({ name: 'resized', params: [data] });
            if (this.resizeCount === this.options.resizeThr || (!this.wrapEl.firstElementChild && this.resizeCount === this.options.resizeThr - 1)) {
                this.setTransInit();
                this.initialResize = true;
                super.listen({ name: 'prepared', params: [data] });
            }
        }, this.options.delay);
        this.mutationListener = debounce((data) => {
            this.mutateCount++;
            for (let k of data) {
                if (k.type === "attributes") {
                    this.updateProg();
                    this.options.bar.enable && this.updateBarProg();
                }
                if (k.type === "childList") {
                    this.options.snap.enable && this.getSnappeds();
                    this.response();
                }
            }
            super.listen({ name: 'refactored', params: [data] });
        }, this.options.delay);
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.setEmpty();
        this.setAttrs();
        this.transNow = this.transLast = this.options.paddingStart;
        this.gap = toPixel(this.options.child.gap);
        this.createBar();
        this.updateDiff();
        this.initSnapArgs();
        this.getSnappeds();
        this.getGestureIns();
        this.setObserver();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    initSnapArgs() {
        if (!this.options.snap.enable)
            return this;
        if (this.options.snap.mode === 'auto') {
            !this.options.snap.selector && (this.options.snap.selector = '[unalignable]');
        }
        else {
            !this.options.snap.selector && (this.options.snap.selector = '[alignable]');
            !this.options.snap.offset && (this.options.snap.offset = 50);
        }
    }
    setEmpty() {
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}scroll`);
        this.wrapEl.classList.add(`${prefix}scroll-wrap`);
        this.targetEl.setAttribute('axis', this.options.axis);
        this.targetEl.toggleAttribute('gridded', this.options.gridded);
    }
    getGestureIns() {
        this.gestureIns = new Gesture(this.wrapEl, extend({
            target: {
                wheel: true,
                keyboard: {
                    enable: this.options.keyboard,
                    prev: this.options.axis === 'x' ? 'ArrowLeft' : 'ArrowUp',
                    next: this.options.axis === 'x' ? 'ArrowRight' : 'ArrowDown',
                    target: this.targetEl,
                },
                spy: this.options.keyboard,
                step: {
                    mode: 'translate',
                    axis: this.options.axis,
                    value: this.options.wheel.value,
                    linkage: false,
                    duration: this.options.duration,
                },
                drift: {
                    enable: this.options.drift.enable,
                    coef: this.options.drift.coef,
                    auto: false,
                },
                swipe: {
                    enable: this.options.swipe.enable,
                    coef: 1,
                    auto: false,
                },
                initialVal: { [this.options.axis]: this.transNow },
                onStep: (data) => {
                    this.getStepVal(data.step.direction[this.options.axis]);
                },
                onStepped: (data) => {
                    this.scrollTo({
                        target: data.translate.value[this.options.axis],
                        duration: this.options.duration,
                        snap: true,
                    });
                },
                onTranslate: (data) => {
                    super.listen({ name: 'start', params: [data.translate] });
                },
                onTranslating: (data) => {
                    if (data.translate.diff.h > 2) {
                        this.transNow = data.translate.value[this.options.axis];
                        this.cross = this.crossLinkage();
                        if (this.cross.includes('baby')) {
                            if (data.pointer === 'wheel')
                                return this;
                            if (this.cross === 'baby-start') {
                                this.transNow = this.getDmpVals('start+');
                            }
                            else if (this.cross === 'baby-start-reverse') {
                                this.transNow = this.getDmpVals('start-');
                            }
                        }
                        else if (this.cross === 'teen-start') {
                            if (data.pointer === 'wheel') {
                                this.toStart();
                                return this;
                            }
                            this.transNow = this.getDmpVals('start+');
                        }
                        else if (this.cross === 'teen-end') {
                            if (data.pointer === 'wheel') {
                                this.toEnd();
                                return this;
                            }
                            this.transNow = this.getDmpVals('end+');
                        }
                        else if (this.cross === 'adult-start') {
                            if (data.pointer === 'wheel') {
                                this.toStart();
                                return this;
                            }
                            this.transNow = this.getDmpVals('start+');
                        }
                        else if (this.cross === 'adult-end') {
                            if (data.pointer === 'wheel') {
                                this.toEnd();
                                return this;
                            }
                            this.transNow = this.getDmpVals('end+');
                        }
                        transformTools.set({
                            el: this.wrapEl,
                            data: { translate: { [this.options.axis]: this.transNow } }
                        });
                        this.exceedHandle({ linkage: this.cross });
                        super.listen({ name: 'move', params: [data.translate] });
                    }
                },
                onTranslated: (data) => {
                    this.transLast = this.transNow;
                    this.exceedHandle({
                        start: async (ratio) => {
                            this.options.b4ToStart && await this.options.b4ToStart.call(this, ratio);
                            this.toStart();
                            this.gestureIns.preventEase = true;
                        },
                        end: async (ratio) => {
                            this.options.b4ToEnd && await this.options.b4ToEnd.call(this, ratio);
                            this.toEnd(undefined, false);
                            this.gestureIns.preventEase = true;
                        },
                        normal: () => {
                            this.options.snap.enable && !data.translate.canDrfit && this.setSnapped();
                        }
                    });
                    super.listen({ name: 'end', params: [data.translate] });
                },
                onSwipe: (data) => {
                    this.setEaseTo(data, 'swipe');
                },
                onDrift: (data) => {
                    this.setEaseTo(data);
                },
            },
            source: this.options.gesture,
        }));
    }
    setTransInit() {
        let target = this.options.initial.value || this.rangeEnd, params = { target, snap: true };
        this.options.initial.mode === 'localte' ? this.locateTo(params) : this.scrollTo(params);
    }
    createBar() {
        if (this.options.bar.enable) {
            if (!this.trackEl) {
                this.thumbEl = createEl('span');
                this.trackEl = createEl('div', { class: `${prefix}scroll-bar` }, this.thumbEl);
                this.options.bar.sticky && this.trackEl.setAttribute('shown', '');
                this.targetEl.appendChild(this.trackEl);
            }
            if (this.options.bar.triggerable && !this.isMobi) {
                let prog = 0;
                this.barIns = new Gesture(this.trackEl, {
                    onTranslate: () => {
                        prog = this.progress;
                        this.trackEl.setAttribute('scrolling', '');
                    },
                    onTranslating: (data) => {
                        this.locateTo({
                            target: prog + data.translate.value[this.options.axis] / this.barSize.diff,
                            isRatio: true,
                        });
                    },
                    onTranslated: () => {
                        this.trackEl.removeAttribute('scrolling');
                    }
                });
            }
        }
    }
    setObserver() {
        this.wrapStyleObs = new MutationObserver(mutations => {
            this.canMutate && this.mutationListener(mutations);
        });
        this.wrapStyleObs.observe(this.wrapEl, {
            childList: true,
            attributes: true,
            attributeFilter: ['style']
        });
        this.sizeObs = new ResizeObserver((entries) => {
            this.canResize && this.resizeListener(entries);
        });
        this.sizeObs.observe(this.targetEl);
        this.sizeObs.observe(this.wrapEl);
    }
    updatePadding(start, end) {
        if (isNull(start) && isNull(end))
            return;
        !isNull(start) && typeof start === 'number' && (this.options.paddingStart = start);
        !isNull(end) && typeof end === 'number' && (this.options.paddingEnd = end);
        this.updateDiff();
        this.transNow < this.rangeStart && this.toEnd(undefined, false);
        this.transNow > this.rangeEnd && this.toStart();
    }
    response() {
        this.updateDiff();
        if (this.isBaby) {
            this.toStart();
        }
        else if (this.wrapEl[this.forwardMap.offset] > this.paddingStart) {
            this.toStart();
        }
        else if (this.transNow < this.rangeStart) {
            this.toEnd(undefined, false);
        }
        else {
            this.options.snap.enable && this.setSnapped();
            
        }
    }
    getDmpVals(placement) {
        let val = 0, dmp = this.options.dmpRatio ** 2;
        if (placement === 'start+') {
            val = this.options.bounce ? this.paddingStart + (this.transNow - this.paddingStart) * dmp : this.paddingStart;
        }
        else if (placement === 'end+') {
            val = this.options.bounce ? this.rangeStart + (this.transNow - this.rangeStart) * dmp : this.rangeStart;
        }
        else if (placement === 'start-') {
            val = this.options.bounce ? (this.transNow - this.paddingStart) * dmp : this.paddingStart;
        }
        return val;
    }
    getTruePadding(val) {
        let result;
        if (val === 'center') {
            let firstSize = ~~(style(this.wrapEl.firstElementChild)[this.forwardMap.size]).replace('px', ''), value = !isNull(this.childAvgSize) ? this.childAvgSize : firstSize;
            result = (this.targetSize - value) / 2;
        }
        else if (typeof val === 'number') {
            if (val <= 0) {
                result = 0;
            }
            else if (val < 1) {
                result = this.targetSize * val;
            }
            else {
                result = val;
            }
        }
        return result;
    }
    setChildSize() {
        let getVal = (val) => {
            let type = typeof val;
            if (type === 'string') {
                if (val.includes('/')) {
                    let avg = ~~(val.split('/')[1].trim());
                    return avg <= 1 ? this.targetSize : (this.targetSize - (avg - 1) * this.gap) / avg;
                }
                else {
                    return toPixel(val);
                }
            }
            else if (type === 'number') {
                return val < 0 ? 0 : (val <= 1 && val >= 0) ? this.targetSize * val : val;
            }
            else {
                return null;
            }
        };
        if (!isEmpty(this.options.child.size)) {
            let children = [...this.wrapEl.children];
            if (Array.isArray(this.options.child.size)) {
                if (this.options.child.settable) {
                    let vals = this.options.child.size.map((k) => getVal(k));
                    children.forEach((k, i) => {
                        !isNull(vals[i]) && (k.style[this.forwardMap.size] = vals[i] + 'px');
                    });
                }
            }
            else {
                let val = getVal(this.options.child.size);
                this.childAvgSize = val;
                if (!isNull(val) && this.options.child.settable) {
                    for (let k of children) {
                        k.style[this.forwardMap.size] = val + 'px';
                    }
                }
            }
        }
    }
    updateDiff() {
        let targetStyle = style(this.targetEl), wrapStyle = style(this.wrapEl), targetHeight = parseInt(targetStyle.height), wrapHeight = parseInt(wrapStyle.height), targetWidth = parseInt(targetStyle.width), wrapWidth = parseInt(wrapStyle.width), outerSize = 0, reverseSize = 0, innerSize = 0;
        if (this.options.axis === 'x') {
            outerSize = targetWidth;
            reverseSize = targetHeight;
            innerSize = wrapWidth;
        }
        else if (this.options.axis === 'y') {
            outerSize = targetHeight;
            reverseSize = targetWidth;
            innerSize = wrapHeight;
        }
        this.targetSize = outerSize;
        this.reverseSize = reverseSize;
        this.wrapSize = innerSize;
        this.setChildSize();
        this.paddingStart = this.getTruePadding(this.options.paddingStart);
        this.paddingEnd = this.getTruePadding(this.options.paddingEnd);
        if (this.paddingStart + this.paddingEnd >= outerSize) {
            this.paddingStart = this.paddingEnd = 0;
            console.warn('The combined padding values on both sides exceed the container size; padding values have been removed!');
        }
        this.rangeEnd = this.paddingStart;
        this.rangeStart = outerSize - innerSize - this.paddingEnd;
        this.activeSize = this.rangeEnd - this.rangeStart;
        this.baseSize = this.targetSize - this.paddingStart - this.paddingEnd;
        this.updateProg();
        this.getBarSize();
        this.updateBarProg();
        this.isBaby = this.rangeStart - this.paddingStart > 0;
    }
    getNowTargetDist(target) {
        if (!target)
            return 0;
        let targetType = getDataType(target);
        return targetType === 'Number' ? Math.abs(target - this.transNow) : targetType.includes('HTML') ? Math.abs(this.getChildParentOffset(target) - this.rangeEnd + this.transNow) : 0;
    }
    setEaseTo(data, type = 'drift') {
        data[type].value[this.options.axis] = this.getEaseDist(data.translate.direction[this.options.axis], this.getEdgeValue(data[type].value[this.options.axis]), type);
        let target = (this.options.snap.enable && this.options.snap.mode === 'auto') ? this.getNearestSnap(data[type].value[this.options.axis]).nearest : data[type].value[this.options.axis];
        this.scrollTo({
            target,
            snap: true,
        });
    }
    getEaseDist(dir, value, type = 'drift') {
        let result = value;
        if (this.options[type].mode === 'ratio') {
            let dft = this.options[type].value || 1;
            result = this.transNow + this.baseSize * dft * dir;
        }
        else if (this.options[type].mode === 'const') {
            this.options[type].value && (result = this.transNow + this.options[type].value * dir);
        }
        else if (this.options[type].mode === 'snap' && this.options.snap.enable && this.options.snap.mode === 'auto') {
            let tmp = this.getEaseNextTrans(dir, type);
            !isNull(tmp) && (result = tmp);
        }
        else if (this.options[type].mode === 'slide') {
            let tmp = this.getEaseNextTrans(dir, type, 'slide');
            !isNull(tmp) && (result = tmp);
        }
        return result;
    }
    getEaseNextTrans(dir, type = 'drift', mode = 'snap') {
        let result = null, chidlren = mode === 'slide' ? [...this.wrapEl.children] : this.snappeds, nearest = mode === 'slide' ? this.getNearestSlide() : this.snapped, idx = chidlren.findIndex((k) => k === nearest);
        if (idx > -1) {
            let num = [0, 1].includes(this.options[type].value) ? 1 : this.options[type].value, dist = dir === 1 ? this.getNowTargetDist(chidlren[idx - num]) : dir === -1 ? this.getNowTargetDist(chidlren[idx + num]) * -1 : 0;
            result = this.transNow + dist;
        }
        return result;
    }
    getStepVal(dir) {
        if (this.options.wheel.mode === 'ratio') {
            let dft = this.options.wheel.value || 0.25, val = this.baseSize * dft;
            this.gestureIns.stepVal = val;
        }
        else if (this.options.wheel.mode === 'const') {
            this.options.wheel.value && (this.gestureIns.stepVal = this.options.wheel.value);
        }
        else if (this.options.wheel.mode === 'snap' && this.options.snap.enable && this.options.snap.mode === 'auto') {
            this.getWheelNextTrans(dir);
        }
        else if (this.options.wheel.mode === 'slide') {
            this.getWheelNextTrans(dir, 'slide');
        }
    }
    getWheelNextTrans(dir, mode = 'snap') {
        let chidlren = mode === 'slide' ? [...this.wrapEl.children] : this.snappeds, nearest = mode === 'slide' ? this.getNearestSlide() : this.snapped, idx = chidlren.findIndex((k) => k === nearest);
        if (idx > -1) {
            let num = [0, 1].includes(this.options.wheel.value) ? 1 : this.options.wheel.value;
            this.gestureIns.stepVal = dir === 1 ? this.getNowTargetDist(chidlren[idx + num]) : dir === -1 ? this.getNowTargetDist(chidlren[idx - num]) : 0;
        }
    }
    getEdgeValue(data, isRatio = false) {
        let value = this.rangeEnd, dataType = getDataType(data);
        if (dataType.includes('HTML') || dataType === 'String') {
            let el = getEl(data);
            el && (value = -this.getChildParentOffset(el) + this.rangeEnd);
        }
        else {
            if (isRatio) {
                let ratio = clampVal({ min: 0, max: 1, val: data });
                value = this.rangeEnd - this.activeSize * ratio;
            }
            else {
                value = data;
            }
        }
        if (value || value === 0) {
            if (value >= this.rangeEnd || this.isBaby) {
                value = this.rangeEnd;
            }
            else if (value <= this.rangeStart) {
                value = this.rangeStart;
            }
        }
        return value;
    }
    updateProg(value = this.transNow) {
        if (this.activeSize <= 0) {
            this.progress = 1;
        }
        else {
            this.progress = (this.rangeEnd - value) / this.activeSize;
        }
    }
    getBarSize() {
        if (this.options.bar.enable) {
            let track = parseInt(style(this.trackEl)[this.forwardMap.size]), ratio = Math.min(this.baseSize / this.wrapSize, 1), thumb = track * ratio;
            this.barSize = { track, thumb, diff: track - thumb };
        }
    }
    updateBarProg() {
        if (this.options.bar.enable) {
            let exceedRatio = this.progress < 0 ? Math.abs(this.progress) : this.progress > 1 ? this.progress - 1 : 0, progress = (this.progress >= 0 || this.progress <= 1) ? this.progress : 1, exceedSize = this.barSize.thumb * (1 - exceedRatio ** 2), thumbPos = (this.barSize.track - exceedSize) * progress;
            this.thumbEl.style[this.forwardMap.size] = exceedSize + 'px';
            this.thumbEl.style[this.forwardMap.position] = thumbPos + 'px';
        }
    }
    exceedListen(ratio, placement) {
        this.listen({ name: 'exceeded', params: [ratio, placement] });
        ratio >= 1 && this.listen({ name: 'completed', params: [ratio, placement] });
    }
    exceedHandle({ start, end, normal, linkage }) {
        let ratio = 0, cross = linkage || this.crossLinkage();
        if (cross.includes('start')) {
            ratio = Math.abs(this.transNow / this.options.completedThr);
            start && start(ratio.toFixed(2));
            linkage && this.exceedListen(toNumber(ratio, { places: 2 }), 'start');
        }
        else if (cross.includes('end')) {
            ratio = Math.abs(this.transNow - this.rangeStart) / this.options.completedThr;
            end && end(ratio.toFixed(2));
            linkage && this.exceedListen(toNumber(ratio, { places: 2 }), 'end');
        }
        else {
            normal && normal();
        }
    }
    crossLinkage(value = this.transNow) {
        let result = 'regular';
        if (this.rangeStart > 0) {
            if (this.isBaby) {
                if (value > this.paddingStart) {
                    result = 'baby-start';
                }
                else {
                    result = 'baby-start-reverse';
                }
            }
            else {
                if (value > this.paddingStart) {
                    result = 'teen-start';
                }
                else if (value < this.rangeStart) {
                    result = 'teen-end';
                }
            }
        }
        else {
            if (value > this.paddingStart) {
                result = 'adult-start';
            }
            else if (value < this.rangeStart) {
                result = 'adult-end';
            }
        }
        return result;
    }
    rollback(dist, duration = this.options.duration) {
        setTimeout(() => {
            duration && (this.wrapEl.style.transitionDuration = duration + 'ms');
            transformTools.set({
                el: this.wrapEl,
                data: { translate: { [this.options.axis]: dist } }
            });
        }, 0);
    }
    toStart(duration, snap = true) {
        if (this.destroyed || this.transNow === this.rangeEnd)
            return this;
        this.transNow = this.transLast = this.paddingStart;
        this.scrollTo({
            snap,
            duration: duration || this.options.duration,
            after: () => {
                super.listen({ name: 'toStart' });
            }
        });
    }
    toEnd(duration, snap = true) {
        if (this.destroyed || this.transNow === this.rangeStart)
            return this;
        this.transNow = this.transLast = this.rangeStart;
        this.scrollTo({
            snap,
            duration: duration || this.options.duration,
            after: () => {
                super.listen({ name: 'toEnd' });
            }
        });
    }
    toPrev() {
        if (this.destroyed || this.transNow === this.rangeEnd)
            return this;
    }
    toNext() {
        if (this.destroyed || this.transNow === this.rangeStart)
            return this;
    }
    getSnappeds() {
        if (this.options.snap.enable) {
            if (this.options.snap.mode === 'auto') {
                this.unsnappeds = super.single2Els(this.options.snap.selector, this.wrapEl);
                this.snappeds = [...this.wrapEl.children].filter(k => !this.unsnappeds.includes(k));
            }
            else {
                this.snappeds = super.single2Els(this.options.snap.selector, this.wrapEl);
            }
            super.listen({ name: 'getSnappeds', params: [this.snappeds] });
        }
    }
    getChildParentOffset(child, parent = this.wrapEl) {
        return child.getBoundingClientRect()[this.forwardMap.position] - parent.getBoundingClientRect()[this.forwardMap.position];
    }
    sortEls(arr, val = this.transNow) {
        let tmp = arr.map((k) => {
            return { el: k, offset: Math.abs(val - this.paddingStart + this.getChildParentOffset(k)) };
        });
        tmp.sort((a, b) => a.offset - b.offset);
        return tmp;
    }
    getNearestSnap(val = this.transNow) {
        let snappeds = this.sortEls(this.snappeds, val), nearest = null;
        if (this.options.snap.mode === 'auto') {
            if (this.options.snap.offset) {
                let temp = snappeds.find((k) => k.offset <= this.options.snap.offset);
                nearest = temp ? temp.el : null;
            }
            else {
                nearest = snappeds[0].el;
            }
        }
        else {
            nearest = snappeds[0].offset <= this.options.snap.offset ? snappeds[0].el : null;
        }
        return { nearest, snappeds };
    }
    getNearestSlide(val = this.transNow) {
        let tmp = this.sortEls([...this.wrapEl.children], val);
        return tmp[0]?.el;
    }
    setSnapped(options = {}) {
        if (this.destroyed || this.wrapEl.children.length === 0 || !this.options.snap.enable)
            return this;
        this.scrolled = false;
        let opt = Object.assign({ force: this.options.snap.force }, options), current = this.getNearestSnap(), snappeds = current.snappeds, nearest = current.nearest, toEl = opt.target ? getEl(opt.target, this.wrapEl) : null, target = toEl || nearest;
        if (!nearest || !target)
            return this;
        if (target !== this.snapped || opt.force) {
            this.toggleSnappedAttr(target, snappeds);
            super.listen({ name: 'snap', cb: opt.before, params: [target] });
        }
        this.scrollTo({
            target,
            duration: opt.duration || this.options.duration,
            after: () => {
                if (target === this.snapped) {
                    super.listen({ name: 'snapped', cb: opt.after, params: [target] });
                }
            }
        });
    }
    toggleSnappedAttr(target, snappeds = this.getNearestSnap().snappeds) {
        let el = getEl(target, this.wrapEl), last;
        if (el) {
            el.setAttribute('snapped', '');
            last = snappeds.find(k => k.el !== el && k.el.hasAttribute('snapped'));
            last && last.el.removeAttribute('snapped');
            this.snapped = el;
        }
    }
    scrollTo({ target = this.transNow, duration, before, after, snap = false, isRatio = false }) {
        if (this.destroyed)
            return this;
        this.scrolled = false;
        let now = this.transNow, trans = this.getEdgeValue(target, isRatio), compare = Math.abs(this.transNow - trans) / this.targetSize, dur = duration || duration == 0 ? duration : Math.trunc(this.options.duration * compare);
        this.wrapEl.style.transitionDuration = clampVal({ val: dur, min: 200, max: 2000 }) + 'ms';
        transformTools.set({ el: this.wrapEl, data: { translate: { [this.options.axis]: trans } } });
        super.listen({ name: 'scroll', cb: before, params: [trans, now] });
        this.transLast = this.transNow = trans;
        delay({
            duration: dur,
            done: () => {
                if (this.options.snap.enable) {
                    snap ? this.setSnapped() : this.getNearestSnap();
                }
                super.listen({ name: 'scrolled', cb: after, params: [trans, now] });
                this.scrolled = true;
            }
        });
    }
    locateTo({ target = this.transNow, before, after, snap = false, isRatio = false }) {
        if (this.destroyed)
            return this;
        let now = this.transNow, trans = this.getEdgeValue(target, isRatio);
        this.wrapEl.style.transitionDuration = '0ms';
        transformTools.set({ el: this.wrapEl, data: { translate: { [this.options.axis]: trans } } });
        super.listen({ name: 'locate', cb: before, params: [trans, now] });
        this.transLast = this.transNow = trans;
        if (this.options.snap.enable) {
            snap ? this.setSnapped() : this.getNearestSnap();
        }
        super.listen({ name: 'located', cb: after, params: [trans, now] });
    }
    
    destroy(cb) {
        if (this.destroyed)
            return this;
        this.sizeObs.disconnect();
        this.wrapStyleObs.disconnect();
        this.options.bar.enable && this.options.bar.triggerable && !this.isMobi && this.barIns.destroy();
        this.gestureIns.destroy();
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

const optMasonry = [
    {
        attr: 'gap',
        prop: 'gap',
        value: '0.8rem',
    },
    {
        attr: 'cols',
        prop: 'cols',
        value: 0,
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'content',
        prop: 'content',
        value: ''
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'tpl-str',
        prop: 'tplStr',
        value: '',
    },
    {
        attr: 'tpl-eng',
        prop: 'tplEng',
        value: null,
    },
    {
        attr: 'media',
        prop: 'media',
        value: {
            caption: '',
            brief: '',
        },
    },
    {
        attr: 'b4-add',
        prop: 'b4Add',
        value: null,
    },
    {
        attr: 'b4-remove',
        prop: 'b4Remove',
        value: null,
    },
    {
        attr: 'b4-clear',
        prop: 'b4Clear',
        value: null,
    },
    {
        attr: 'on-added',
        prop: 'onAdded',
        value: null,
    },
    {
        attr: 'on-removed',
        prop: 'onRemoved',
        value: null,
    },
    {
        attr: 'on-cleared',
        prop: 'onCleared',
        value: null,
    },
    {
        attr: 'on-updatedcont',
        prop: 'onUpdatedCont',
        value: null,
    },
    ...optBase
];

class Masonry extends ModBaseListenCache {
    options = {};
    itemsTmp;
    itemsObs;
    items;
    itemSizeObs;
    gap;
    static hostType = 'node';
    static optMaps = optMasonry;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            maps: Masonry.optMaps,
            host: elem,
            component: false,
        });
        this.itemsTmp = [];
        this.itemsObs = new Observe(this.itemsTmp, {
            onSet: (data) => {
                if (getDataType(data.value).includes('HTML')) {
                    this.targetEl.appendChild(data.value);
                    this.itemSizeObs.observe(data.value);
                }
            },
            onDeleted: (data) => { },
            onCompleted: () => {
            }
        });
        this.items = this.itemsObs.proxy;
        this.itemSizeObs = new ResizeObserver((data) => {
            for (let k of data) {
                this.updateItemStyle(k.target);
            }
        });
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.gap = Math.ceil(toPixel(this.options.gap));
        this.setAttrs();
        if (Array.isArray(this.options.content) && this.options.content.length > 0) {
            this.items.push(...this.options.content);
        }
        else if (this.targetEl.innerHTML.trim()) {
            this.items.push(...this.targetEl.children);
        }
        else {
            return this;
        }
        super.listen({ name: 'initiated', cb });
        return this;
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}masonry`);
        classes(this.targetEl).add(this.options.classes);
        this.targetEl.style.columnGap = `${this.gap}px`;
        this.options.cols ? addStyle(this.targetEl, 'grid-template-columns', `repeat(${this.options.cols}, 1fr)`) : removeStyle(this.targetEl, 'grid-template-columns');
    }
    updateItemStyle(el) {
        let height = el.offsetHeight;
        el.style.gridRow = `span ${Math.ceil(height + this.gap)}`;
    }
    setItem(el) {
        this.itemSizeObs.observe(el);
    }
    async add(data, cb) {
        if (this.destroyed)
            return this;
        let tmp = Array.isArray(data) ? data : [data];
        this.options.b4Add && await this.options.b4Add.call(this, tmp);
        this.items.push(...tmp);
        super.listen({ name: 'add', cb, params: [tmp] });
        return this;
    }
    async remove(data, cb) {
        if (this.destroyed)
            return this;
        let tmp = Array.isArray(data) ? data : [data];
        this.options.b4Remove && await this.options.b4Remove.call(this, tmp);
        for (let k = 0; k < tmp.length; k++) {
            let item = tmp[k], idx = this.items.findIndex((k) => k === item);
            if (idx > -1) {
                this.items.splice(idx, 1);
                this.items[idx].remove();
            }
        }
        super.listen({ name: 'remove', cb, params: [tmp] });
        return this;
    }
    async clear(cb) {
        if (this.destroyed)
            return this;
        this.options.b4Clear && await this.options.b4Clear.call(this);
        this.items.splice(0);
        this.targetEl.innerHTML = '';
        super.listen({ name: 'cleared', cb });
        return this;
    }
    
    destroy(cb) {
        if (this.destroyed)
            return this;
        this.clearCache();
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

let AXTMP_hyphen$2 = config.splitHyphen;
const optSwipe = [
    {
        attr: 'axis',
        prop: 'axis',
        value: 'x',
    },
    {
        attr: 'keyboard',
        prop: 'keyboard',
        value: false,
    },
    {
        attr: 'active',
        prop: 'active',
        value: 0,
    },
    {
        attr: 'duration',
        prop: 'duration',
        value: 500,
    },
    {
        attr: 'throttle',
        prop: 'throttle',
        value: 0,
    },
    {
        attr: 'slides',
        prop: 'slides',
        value: 1,
    },
    {
        attr: 'divide',
        prop: 'divide',
        value: 1,
    },
    {
        attr: 'group',
        prop: 'group',
        value: 0,
    },
    {
        attr: 'loop',
        prop: 'loop',
        value: false,
    },
    {
        attr: 'autoHeight',
        prop: 'autoHeight',
        value: false,
    },
    {
        attr: 'countdown',
        prop: 'countdown',
        value: {},
    },
    {
        attr: 'gap',
        prop: 'gap',
        value: 0,
    },
    {
        attr: 'unalign',
        prop: 'unalign',
        value: '[unalignable]',
    },
    {
        attr: 'centered',
        prop: 'centered',
        value: {
            enable: false,
            fill: false,
        },
    },
    {
        attr: 'active',
        prop: 'active',
        value: 0,
    },
    {
        attr: 'keyboard',
        prop: 'keyboard',
        value: false,
    },
    {
        attr: 'resize-reset',
        prop: 'resizeReset',
        value: true,
    },
    {
        attr: 'drift',
        prop: 'drift',
        value: 1,
    },
    {
        attr: 'wheel',
        prop: 'wheel',
        value: 1,
    },
    {
        attr: 'scroll',
        prop: 'scroll',
        value: {},
    },
    {
        attr: 'tools',
        prop: 'tools',
        value: {
            enable: false,
            placement: 'right-top',
            children: [],
        },
    },
    {
        attr: 'total',
        prop: 'total',
        value: {
            enable: false,
            placement: 'right-top',
            hyphen: '/',
        },
    },
    {
        attr: 'pgn',
        prop: 'pgn',
        value: {
            enable: false,
            classes: '',
            type: 'lamp',
            selector: '',
            data: [],
            before: null,
            after: null,
        },
    },
    {
        attr: 'nav',
        prop: 'nav',
        value: {
            enable: true,
            classes: '',
            fill: false,
            intvl: 500,
            prev: {
                selector: '',
                classes: '',
                text: '',
            },
            next: {
                selector: '',
                classes: '',
                text: '',
            },
        },
    },
    {
        attr: 'autoplay',
        prop: 'autoplay',
        value: {
            enable: false,
            delay: 4000,
            countdown: false,
        },
    },
    {
        attr: 'integrated',
        prop: 'integrated',
        value: false,
    },
    {
        attr: 'content',
        prop: 'content',
        value: '',
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'hyphen',
        prop: 'hyphen',
        value: AXTMP_hyphen$2,
    },
    {
        attr: 'tpl-str',
        prop: 'tplStr',
        value: '',
    },
    {
        attr: 'tpl-eng',
        prop: 'tplEng',
        value: null,
    },
    {
        attr: 'lazy',
        prop: 'lazy',
        value: false,
    },
    {
        attr: 'media',
        prop: 'media',
        value: {
            caption: '',
            brief: '',
        },
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'on-fold',
        prop: 'onFold',
        value: null,
    },
    {
        attr: 'on-unfold',
        prop: 'onUnfold',
        value: null,
    },
    {
        attr: 'on-updatedcont',
        prop: 'onUpdatedCont',
        value: null,
    },
    ...optBase
];

const getSlidersData = async function (opts) {
    let wrapEl = getEl(opts.wrap), parentEl = getEl(opts.parent), grossData = [];
    if (!isEmpty(opts.content)) {
        await getContent.call(this, {
            content: opts.content,
            contType: opts.contType,
            contData: opts.contData,
            hyphen: opts.hyphen,
            ajax: {
                xhrName: 'contXhr',
                spinSel: parentEl,
                ...(opts.ajax || {})
            },
            cb: (data) => {
                grossData = data;
            }
        });
    }
    else if (wrapEl && wrapEl.innerHTML.trim()) {
        grossData = [...wrapEl.children];
    }
    else {
        grossData = [];
    }
    let fillSlider = async (node, data) => {
        let childType = getDataType(data);
        if (childType === 'String' || childType === 'Number') {
            
            node.innerHTML = data;
        }
        else if (childType === 'Object') {
            if (data.hasOwnProperty('content')) {
                await getContent.call(this, {
                    content: data.content,
                    contType: data.contType,
                    contData: data.contData,
                    hyphen: data.hyphen || opts.hyphen,
                    ajax: {
                        xhrName: 'contXhr',
                        spinSel: node,
                        ...(data.ajax || {})
                    },
                    cb: (resp) => {
                        let respType = getDataType(resp);
                        if (['String', 'Number'].includes(respType)) {
                            node.innerHTML = resp;
                        }
                        else if (respType.includes('HTML')) {
                            node.appendChild(resp);
                        }
                        else if (['Object', 'Array'].includes(respType)) {
                            node.innerHTML = getStrFromTpl.call(this, resp, data.tplStr, data.tplEng);
                        }
                    }
                });
            }
            else {
                node.innerHTML = getStrFromTpl.call(this, data);
            }
        }
        else if (childType === 'Array') {
            node.innerHTML = getStrFromTpl.call(this, data);
        }
    }, dataType = getDataType(grossData), result = [];
    if (dataType.includes('HTML')) {
        result = [{ source: grossData, slider: grossData }];
    }
    else if (dataType === 'String') {
        let tmp = createEl('div', '', grossData), els = [...(tmp.querySelector('ul,ol') || tmp).children];
        result = els.map((k) => {
            return { source: k, slider: k };
        });
    }
    else if (dataType === 'Array' || dataType === 'Object') {
        let tmp = (dataType === 'Object') ? [grossData] : grossData.filter((k) => !isNull(k));
        if (tmp.length === 0)
            return;
        result = tmp.map((k) => {
            let childType = getDataType(k), slider;
            if (childType.includes('HTML')) {
                slider = k;
            }
            else {
                slider = createEl('li');
                if (opts.lazy) {
                    k.spy = new Spy(slider, {
                        root: parentEl || wrapEl,
                        repeat: false,
                        onIn: () => {
                            fillSlider(slider, k);
                        }
                    });
                }
                else {
                    fillSlider(slider, k);
                }
            }
            return { source: k, slider };
        });
    }
    opts.cb && opts.cb(result);
    return result;
};

const optProgress = [
    {
        attr: 'type',
        prop: 'type',
        value: 'line',
    },
    {
        attr: 'theme',
        prop: 'theme',
        value: '',
    },
    {
        attr: 'gradient',
        prop: 'gradient',
        value: false
    },
    {
        attr: 'linecap',
        prop: 'linecap',
        value: 'round',
    },
    {
        attr: 'size',
        prop: 'size',
        value: '',
    },
    {
        attr: 'thk',
        prop: 'thk',
        value: '',
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'range',
        prop: 'range',
        value: [0, 100],
    },
    {
        attr: 'value',
        prop: 'value',
        value: 0,
    },
    {
        attr: 'curve',
        prop: 'curve',
        value: 'easeOut',
    },
    {
        attr: 'duration',
        prop: 'duration',
        value: 0,
    },
    {
        attr: 'rotate',
        prop: 'rotate',
        value: false,
    },
    {
        attr: 'simple',
        prop: 'simple',
        value: false,
    },
    {
        attr: 'control',
        prop: 'control',
        value: false,
    },
    {
        attr: 'steps',
        prop: 'steps',
        value: [],
    },
    {
        attr: 'label',
        prop: 'label',
        value: {
            enable: true,
            template: '',
            placement: '',
            unit: '%',
            tips: false,
        },
    },
    {
        attr: 'number',
        prop: 'number',
        value: {
            type: 'percent',
            trunc: true,
            emphatic: false,
        },
    },
    {
        attr: 'b4-set',
        prop: 'b4Set',
        value: null,
    },
    {
        attr: 'on-start',
        prop: 'onStart',
        value: null,
    },
    {
        attr: 'on-move',
        prop: 'onMove',
        value: null,
    },
    {
        attr: 'on-end',
        prop: 'onEnd',
        value: null,
    },
    {
        attr: 'on-zero',
        prop: 'onZero',
        value: null,
    },
    {
        attr: 'on-completed',
        prop: 'onCompleted',
        value: null,
    },
    {
        attr: 'on-set',
        prop: 'onSet',
        value: null,
    },
    {
        attr: 'on-pause',
        prop: 'onPause',
        value: null,
    },
    {
        attr: 'on-continue',
        prop: 'onContinue',
        value: null,
    },
    {
        attr: 'on-restored',
        prop: 'onRestored',
        value: null,
    },
    ...optBase
];

class Progress extends ModBaseListenCache {
    options = {};
    vals;
    valNow;
    barNow;
    range;
    barSize;
    steps;
    completeEl;
    textEl;
    controlEl;
    obsIns;
    obs;
    labelTpl;
    trackEl;
    wrapEl;
    barEl;
    labelEl;
    numEl;
    unitEl;
    tipsEl;
    animFrame;
    durRest;
    initVals;
    startVals;
    endVals;
    toggleCtrl;
    static hostType = 'node';
    static optMaps = optProgress;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            maps: Progress.optMaps,
            host: elem,
            component: true,
            spread: ['label']
        });
        this.vals = {};
        this.valNow = 0;
        this.barNow = 0;
        this.toggleCtrl = () => {
            this.controlEl.classList.contains(`${prefix}icon-pause`) ? this.pause() : this.continue();
        };
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.range = [...this.options.range].sort((a, b) => a - b);
        this.barSize = this.options.type === 'circle' ? 251.2 : this.options.type === 'semicircle' ? 125.6 : this.options.type === 'gapcircle' ? 167.6 : this.range[1];
        this.steps = this.options.steps.length ? [...this.options.steps].sort((a, b) => b.value - a.value) : [];
        this.getInitVals();
        this.completeEl = createEl('span', { [alias]: 'complete' }, this.options.lang.complete);
        this.textEl = createEl('div', { [alias]: 'text' });
        this.controlEl = createEl('i', { [alias]: 'control', class: `${prefix}icon-pause`, disabled: '' });
        this.controlEl.addEventListener('click', this.toggleCtrl, false);
        this.setObs();
        this.render();
        this.setAttrs();
        this.watchEnds();
        this.watchSteps();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    setObs() {
        this.obsIns = new Observe({ paused: false, completed: false }, {
            onSet: (resp) => {
                if (resp.key === 'paused') ;
                else if (resp.key === 'completed') {
                    if (resp.value) {
                        this.targetEl.setAttribute('completed', '');
                        this.labelEl ? this.labelEl.appendChild(this.completeEl) : null;
                    }
                    else {
                        this.completeEl.remove();
                        this.targetEl.removeAttribute('completed');
                    }
                }
            }
        });
        this.obs = this.obsIns.proxy;
    }
    setControl(pause, disabled) {
        if (pause) {
            classes(this.controlEl).replace(`${prefix}icon-pause`, `${prefix}icon-play`);
        }
        else {
            classes(this.controlEl).replace(`${prefix}icon-play`, `${prefix}icon-pause`);
        }
        if (disabled) {
            this.controlEl.setAttribute('disabled', '');
        }
        else {
            this.controlEl.removeAttribute('disabled');
        }
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}progress`);
        this.targetEl.setAttribute('linecap', this.options.linecap);
        this.targetEl.setAttribute('type', this.options.type);
        this.options.thk ? this.targetEl.setAttribute('thk', this.options.thk) : this.targetEl.removeAttribute('thk');
        this.options.size ? this.targetEl.setAttribute('size', this.options.size) : this.targetEl.removeAttribute('size');
        this.options.theme ? this.targetEl.setAttribute('theme', this.options.theme) : this.targetEl.removeAttribute('theme');
        this.targetEl.toggleAttribute('gradient', this.options.gradient);
        this.targetEl.toggleAttribute('simple', this.options.simple);
        this.options.classes && classes(this.targetEl).add(this.options.classes);
        let tmp = [`${prefix}rotate360`, `${prefix}loop-inf`];
        (this.options.rotate && this.options.type === 'circle') ? this.wrapEl.classList.add(...tmp) : this.wrapEl.classList.remove(...tmp);
    }
    getInitVals() {
        this.vals = this.getVals();
        this.valNow = this.vals.val;
        this.barNow = this.vals.bar;
        this.initVals = { ...this.vals };
        this.startVals = {
            val: this.range[0],
            bar: this.barSize,
            ratio: 0,
            percent: 0,
            number: this.getNumber(this.range[0], 0),
        };
        this.endVals = {
            val: this.range[1],
            bar: 0,
            ratio: 1,
            percent: 100,
            number: this.getNumber(this.range[1], 100),
        };
    }
    getVal(val = this.vals.val) {
        return isNull(val) ? this.range[0] : clampVal({ val, min: this.range[0], max: this.range[1] });
    }
    getBarVal(val) {
        let result;
        if (val == this.range[0]) {
            result = this.barSize;
        }
        else if (val == this.range[1]) {
            result = 0;
        }
        else {
            let value, ratio, tmp;
            if (isNull(val)) {
                value = this.vals.val;
                ratio = this.vals.ratio;
            }
            else {
                value = clampVal({ val: val, min: this.range[0], max: this.range[1] });
                ratio = this.getRatio(value);
            }
            tmp = (this.options.type == 'line') ? (value - this.range[0]) : (this.barSize * (1 - ratio));
            result = toNumber(tmp, { mode: 'floor' });
        }
        return result;
    }
    getRatio(val = this.vals.val) {
        let result;
        if (val == this.range[1]) {
            result = 1;
        }
        else if (val == this.range[0]) {
            result = 0;
        }
        else {
            result = toNumber((val - this.range[0]) / (this.range[1] - this.range[0]), { places: 4, mode: 'floor' });
        }
        return result;
    }
    getNumber(value, percent) {
        let tmp = this.options.number.type === 'value' ? value : percent, val = this.options.number.trunc ? ~~tmp : toNumber(tmp, { places: 2, mode: 'floor', zero: true }), result = this.options.number.emphatic ? splitNum(val) : val;
        return result;
    }
    getVals(val = this.options.value) {
        let value = this.getVal(val), ratio = this.getRatio(value), percent = ratio * 100, barVal = this.getBarVal(value), number = this.getNumber(value, percent);
        return {
            val: value,
            bar: barVal,
            ratio,
            percent,
            number,
        };
    }
    render() {
        this.targetEl.innerHTML = '';
        this.labelTpl = this.options.label.template || `<div ${alias}=label><div ${alias}=result><i ${alias}=number></i><i ${alias}=unit></i></div></div>`;
        if (this.options.type == 'line') {
            this.trackEl = createEl('div', { [alias]: 'track' });
            this.wrapEl = this.trackEl;
            this.barEl = createEl('div', { [alias]: 'bar', 'style': `width:${this.vals.percent}%` });
            this.wrapEl.appendChild(this.barEl);
        }
        else {
            let gradient = '', svg = '';
            if (this.options.gradient) {
                if (this.options.theme == 'prim') {
                    gradient = `
                <linearGradient id="svgbar-prim" >
                    <stop offset="0%" style="stop-color:var(--${prefix}c-prim);" ></stop>
                    <stop offset="100%" style="stop-color:var(--${prefix}c-prim-aj);" ></stop>
                </linearGradient>
              `;
                }
                else if (this.options.theme == 'error') {
                    gradient = `
                <linearGradient id="svgbar-error" >
                    <stop offset="0%" style="stop-color:var(--${prefix}c-error);" ></stop>
                    <stop offset="100%" style="stop-color:var(--${prefix}c-error-aj);" ></stop>
                </linearGradient>
              `;
                }
                else if (this.options.theme == 'succ') {
                    gradient = `
                <linearGradient id="svgbar-succ" >
                    <stop offset="0%" style="stop-color:var(--${prefix}c-succ);" ></stop>
                    <stop offset="100%" style="stop-color:var(--${prefix}c-succ-aj);" ></stop>
                </linearGradient>
              `;
                }
                else if (this.options.theme == 'warn') {
                    gradient = `
            <linearGradient id="svgbar-warn" >
                <stop offset="0%" style="stop-color:var(--${prefix}c-warn);" ></stop>
                <stop offset="100%" style="stop-color:var(--${prefix}c-warn-aj);" ></stop>
            </linearGradient>
          `;
                }
                else if (this.options.theme == 'info') {
                    gradient = `
                <linearGradient id="svgbar-info" >
                    <stop offset="0%" style="stop-color:var(--${prefix}c-info);" ></stop>
                    <stop offset="100%" style="stop-color:var(--${prefix}c-info-aj);" ></stop>
                </linearGradient>
              `;
                }
                else if (this.options.theme == 'issue') {
                    gradient = `
                <linearGradient id="svgbar-issue" >
                    <stop offset="0%" style="stop-color:var(--${prefix}c-issue);" ></stop>
                    <stop offset="100%" style="stop-color:var(--${prefix}c-issue-aj);"></stop>
                </linearGradient>
              `;
                }
            }
            if (this.options.type == 'circle') {
                svg = `<svg viewBox="0 0 100 100">
                            ${gradient}
                            ${!this.options.simple ? `<path d="M 50 50 m -40 0 a 40 40 0 1 1 80 0  a 40 40 0 1 1 -80 0" ${alias}="track"></path>` : ''}
                            <path d="M 50 50 m -40 0 a 40 40 0 1 1 80 0  a 40 40 0 1 1 -80 0" ${alias}="bar" stroke-dasharray=${this.barSize} stroke-linecap=${this.options.linecap}  stroke-dashoffset=${this.vals.bar}></path>
                       </svg>
                        `;
            }
            else if (this.options.type == 'semicircle') {
                svg = `<svg viewBox="0 0 100 60">
                            ${gradient}
                            ${!this.options.simple ? `<path d="M 50 50 m -40 0 a 40 40 0 1 1 80 0" ${alias}="track" stroke-linecap=${this.options.linecap}></path>` : ''}
                            <path d="M 50 50 m -40 0 a 40 40 0 1 1 80 0" ${alias}="bar" stroke-dasharray=${this.barSize} stroke-linecap=${this.options.linecap} stroke-dashoffset=${this.vals.bar}></path>
                       </svg>
                        `;
            }
            else if (this.options.type == 'gapcircle') {
                svg = `<svg viewBox="0 0 100 80">
                            ${gradient}
                            ${!this.options.simple ? `<path d="M15.367,70.026C11.954,64.137,10,57.296,10,49.999 C10,27.909,27.909,10,50,10c22.092,0,40,17.909,40,39.999c0,7.295-1.952,14.134-5.363,20.022" ${alias}="track" stroke-linecap=${this.options.linecap}></path>` : ''}
                            <path d="M15.367,70.026C11.954,64.137,10,57.296,10,49.999 C10,27.909,27.909,10,50,10c22.092,0,40,17.909,40,39.999c0,7.295-1.952,14.134-5.363,20.022" ${alias}="bar" stroke-dasharray=${this.barSize} stroke-linecap=${this.options.linecap} stroke-dashoffset=${this.vals.bar}></path>
                       </svg>
                        `;
            }
            this.wrapEl = tplToEl(svg);
            this.barEl = this.wrapEl.querySelector(`[${alias}="bar"]`);
            this.trackEl = this.wrapEl.querySelector(`[${alias}="track"]`);
        }
        this.targetEl.appendChild(this.wrapEl);
        this.targetEl.appendChild(this.textEl);
        if (this.options.label.enable) {
            if (!this.options.label.placement) {
                this.textEl.insertAdjacentHTML('beforeEnd', this.labelTpl);
                this.labelEl = this.targetEl.querySelector(`[${alias}="label"]`);
            }
            else {
                let place = getEl(this.options.label.placement);
                if (place) {
                    place.insertAdjacentHTML('beforeEnd', this.labelTpl);
                    this.labelEl = place.querySelector(`[${alias}="label"]`);
                }
            }
            this.numEl = this.labelEl.querySelector(`[${alias}="number"]`);
            this.unitEl = this.labelEl.querySelector(`[${alias}="unit"]`);
            this.numEl && (this.numEl.innerHTML = this.vals.number);
            this.unitEl && (this.unitEl.innerHTML = this.options.label.unit);
            if (this.options.label.tips) {
                if (!this.labelEl.querySelector(`[${alias}="tips"]`)) {
                    this.tipsEl = createEl('span', { [alias]: 'tips' }, this.options.lang.tips);
                    this.labelEl.insertAdjacentElement('afterbegin', this.tipsEl);
                }
                else {
                    this.tipsEl = this.labelEl.querySelector(`[${alias}="tips"]`);
                    this.tipsEl.innerHTML = this.options.lang.tips;
                }
            }
        }
        this.options.control && this.textEl.appendChild(this.controlEl);
    }
    setAnim(val = this.vals.val, duration) {
        if (this.obs.paused)
            return false;
        if (val === this.valNow)
            return false;
        this.animFrame && cancelAnimationFrame(this.animFrame);
        let vals = this.vals, dftTime = (size) => size * 3 + 200, barDiff = vals.bar - this.barNow, time = duration || dftTime(Math.abs(barDiff));
        this.setControl(false, false);
        ease({
            from: { val: this.valNow },
            to: { val },
            duration: time,
            curve: this.options.curve,
            before: (resp) => {
                super.listen({ name: 'start', params: [{ ...vals }] });
            },
            progress: (resp) => {
                vals = this.getVals(resp.value.val);
                this.durRest = resp.remaining;
                this.valNow = vals.val;
                this.barNow = vals.bar;
                if (this.options.type == 'line') {
                    this.barEl.style.width = vals.percent + '%';
                }
                else {
                    this.barEl.setAttribute('stroke-dashoffset', this.barNow);
                }
                this.numEl && (this.numEl.innerHTML = vals.number);
                this.animFrame = resp.frame;
                this.watchEnds();
                this.watchSteps();
            },
            doing: (resp) => {
                super.listen({ name: 'move', params: [{ ...this.vals, valNow: this.valNow, barNow: this.barNow }] });
            },
            done: (resp) => {
                this.setControl(false, true);
                super.listen({ name: 'end', params: [{ ...this.vals, valNow: this.valNow, barNow: this.barNow }] });
            },
        });
    }
    watchEnds() {
        if (this.valNow >= this.range[1]) {
            this.obs.completed = true;
            super.listen({ name: 'completed' });
        }
        else {
            this.obs.completed = false;
            (this.valNow <= this.range[0]) && super.listen({ name: 'zero' });
        }
    }
    watchSteps(val = this.valNow) {
        if (!this.steps.length)
            return;
        let step = this.steps.find((k) => k.value <= val);
        if (step) {
            if (step.color) {
                if (['prim', 'succ', 'error', 'info', 'warn', 'issue', 'text'].includes(step.color)) {
                    this.targetEl.setAttribute('theme', step.color);
                }
                else {
                    if (this.options.type === 'line') {
                        this.barEl.style.background = step.color;
                    }
                    else {
                        this.barEl.style.stroke = step.color;
                    }
                }
            }
            step.text && (this.textEl.innerHTML = step.text);
            step.action && step.action.call(this, step.value, this.valNow);
        }
    }
    getStepVals(val) {
        let result;
        if (typeof val === 'string') {
            if (val === 'zero') {
                result = this.startVals;
            }
            else if (val === 'completed') {
                result = this.endVals;
            }
            else if (val.includes('/')) {
                let splits = val.split('/'), num = ~~splits[0], demom = ~~splits[1], ratio = num / demom, value = (this.range[1] - this.range[0]) * ratio + this.range[0];
                result = this.getVals(value);
            }
            else {
                result = this.getVals();
            }
        }
        else if (typeof val === 'number') {
            result = this.getVals(val);
        }
        return result;
    }
    async animateTo(val, opts) {
        if (this.destroyed)
            return this;
        this.barEl.style.transitionDuration = null;
        this.obs.paused = false;
        this.vals = typeof val === 'string' ? this.getStepVals(val) : this.getVals(val);
        this.options.b4Set && await this.options.b4Set.call(this, this.vals);
        if (this.vals.val === this.valNow)
            return false;
        this.setAnim(this.vals.val, opts?.duration);
        super.listen({ name: 'set', cb: opts?.cb, params: [{ ...this.vals, valNow: this.valNow, barNow: this.barNow }] });
        return this;
    }
    async locateTo(val, opts) {
        this.vals = typeof val === 'string' ? this.getStepVals(val) : this.getVals(val);
        this.options.b4Set && await this.options.b4Set.call(this, this.vals);
        this.animFrame && cancelAnimationFrame(this.animFrame);
        this.obs.paused = false;
        this.valNow = this.vals.val;
        this.barNow = this.vals.bar;
        this.numEl && (this.numEl.innerHTML = this.vals.number);
        this.barEl.style.transitionDuration = '0ms';
        if (this.options.type === 'line') {
            this.barEl.style.width = this.vals.percent + '%';
        }
        else {
            this.barEl.setAttribute('stroke-dashoffset', this.barNow);
        }
        this.watchEnds();
        this.watchSteps();
        super.listen({ name: 'set', cb: opts?.cb, params: [{ ...this.vals, valNow: this.valNow, barNow: this.barNow }] });
        return this;
    }
    restore(cb) {
        if (this.destroyed)
            return this;
        this.locateTo('restore');
        super.listen({ name: 'restored', cb });
        return this;
    }
    setCompleted(cb) {
        if (this.destroyed)
            return this;
        this.locateTo('completed');
        cb && cb.call(this);
        return this;
    }
    setZero(cb) {
        if (this.destroyed)
            return this;
        this.locateTo('zero');
        cb && cb.call(this);
        return this;
    }
    continue(duration, cb) {
        if (this.destroyed || this.valNow === this.vals.val)
            return this;
        if (!this.obs.paused)
            return false;
        this.obs.paused = false;
        this.setControl(false, false);
        this.setAnim(this.vals.val, duration || this.durRest);
        super.listen({ name: 'continue', cb, params: [{ ...this.vals, valNow: this.valNow, barNow: this.barNow, durRest: duration || this.durRest }] });
        return this;
    }
    pause(cb) {
        if (this.destroyed || this.valNow === this.vals.val)
            return this;
        if (this.animFrame) {
            cancelAnimationFrame(this.animFrame);
            this.obs.paused = true;
            this.setControl(true, false);
        }
        super.listen({ name: 'pause', cb, params: [{ ...this.vals, valNow: this.valNow, barNow: this.barNow }] });
        return this;
    }
    get() {
        return {
            ...this.vals,
            valNow: this.valNow,
            barNow: this.barNow,
            completed: this.obs.completed,
            paused: this.obs.paused,
            durRest: this.durRest || 0,
        };
    }
    
    destroy(cb) {
        if (this.destroyed)
            return this;
        this.restore();
        this.controlEl.removeEventListener('click', this.toggleCtrl);
        this.obsIns.ins.destroy();
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

class Swipe extends ModBaseListenCache {
    options = {};
    intercepted;
    button;
    content;
    folded;
    handlers;
    html;
    pureHtml;
    event;
    text;
    ell;
    countdownEl;
    intervalEvt;
    startClone;
    endClone;
    sliderTpl;
    prepareListen;
    resetListener;
    setBulletActive;
    spyIns;
    slidersTmp;
    bulletsTmp;
    paused;
    scrollOpt;
    scrollIns;
    wrapEl;
    navEl;
    pgnEl;
    pgnIns;
    prevEl;
    nextEl;
    totalEl;
    numEl;
    denomEl;
    bullets;
    bulletSeq;
    interval;
    countdownIns;
    toolsEl;
    keyToSwipe;
    sliders;
    group;
    actIdx;
    slidersObs;
    bulletsObs;
    slideSize;
    slideSizeDivide;
    slides;
    totalClone;
    static hostType = 'node';
    static optMaps = optSwipe;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            maps: Swipe.optMaps,
            host: elem,
            component: true,
            spread: ['autoplay', 'total', 'tools', 'pgn', 'nav', 'centered']
        });
        this.countdownEl = createEl('div');
        this.intervalEvt = () => {
            this.toNextBullet();
            this.startCountdown();
        };
        this.startClone = [];
        this.endClone = [];
        this.sliderTpl = { label: '', content: '', thumb: '', brief: '', slider: null, cb: null };
        this.prepareListen = throttle(() => {
            this.fillWedgets();
            this.setBulletActive(this.bullets.find((k) => this.actIdx === k.index) || 0);
            this.toggleNavDisabled();
            this.targetEl.classList.remove(`${prefix}initiating`);
            this.setAutoPlay();
        });
        this.resetListener = throttle(() => {
            this.getSingleSize();
            this.setSlideSize();
            (this.group > this.sliders.length) && (this.group = this.sliders.length);
            super.listen({ name: 'resized' });
        });
        this.setBulletActive = (data) => {
            let bullet = this.findBullet(data);
            if (!bullet || bullet.active)
                return;
            bullet.active = true;
            let other = this.bullets.find((k) => k !== bullet && k.active);
            other && Reflect.deleteProperty(other, 'active');
            
        };
        super.listen({ name: 'constructed' });
        if (initial) {
            if (this.options.lazy) {
                this.spyIns = new Spy(this.targetEl, {
                    repeat: false,
                    onIn: () => {
                        this.init();
                    }
                });
            }
            else {
                this.init();
            }
        }
    }
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.slidersTmp = [];
        this.bulletsTmp = [];
        this.paused = true;
        this.setAttrs();
        this.createWrap();
        await this.getSliders(this.options.content, (resp) => {
            this.slidersTmp = resp;
        });
        this.setSlidesObs();
        this.setBulletsObs();
        this.actIdx = this.getIdxFromActive(this.options.active);
        this.appendSliders();
        this.group = clampVal({ val: this.options.group, min: 0, max: this.sliders.length });
        this.setScroll();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    
    
    setAttrs() {
        classes(this.targetEl).add(`${prefix}swipe,${prefix}initiating`);
    }
    getDriftParams() {
        if (this.group)
            return { enable: true, mode: 'snap', value: 1 };
        let tmp = { mode: 'auto', coef: 6 };
        if (typeof this.options.drift === 'number' && this.options.drift > 0) {
            tmp = { mode: 'snap', value: this.options.divide * this.options.drift };
        }
        else if (this.options.drift === 'viewport' && this.options.slides > 0) {
            tmp = { mode: 'snap', value: this.options.slides * this.options.divide };
        }
        return { enable: true, ...tmp };
    }
    getWheelVal() {
        return this.options.divide * (this.group ? 1 : this.options.wheel);
    }
    setScroll() {
        this.scrollOpt = extend({
            target: {
                wrapper: this.wrapEl,
                gridded: this.options.divide > 1 ? true : false,
                axis: this.options.axis,
                keyboard: this.options.keyboard,
                snap: {
                    enable: true,
                    selector: this.options.unalign,
                },
                wheel: {
                    mode: 'snap',
                    value: this.getWheelVal(),
                },
                drift: this.getDriftParams(),
                initial: {
                    value: this.getInitSlider(),
                },
                duration: this.options.duration,
                resizeThr: this.options.slides > 0 ? 2 : 1,
                paddingStart: this.options.centered.enable ? 'center' : 0,
                paddingEnd: this.options.centered.enable ? 'center' : 0,
                child: { size: (this.options.slides > 0 ? `1 / ${this.options.slides}` : null), gap: this.options.gap },
                onGetSnappeds: function () {
                },
                onPrepared: this.prepareListen,
                onRefactored: this.resetListener,
                onResized: this.resetListener,
                onSnap: (slider) => {
                    if (this.scrollIns.initialResize) {
                        this.actIdx = this.sliders.findIndex((k) => k.slider === slider);
                        this.setBulletActive(slider);
                    }
                    (this.options.autoHeight && this.options.axis === 'x') && (this.targetEl.style.height = style(slider).height);
                },
                onSnapped: (slider) => {
                },
                onMove: () => {
                    this.pause(true);
                }
            },
            source: this.options.scroll
        });
        if (this.group && this.options.slides > 1) ;
        if (this.options.slides >= 1) ;
        this.scrollIns = new Scroll(this.targetEl, this.scrollOpt);
    }
    async getSliders(data = this.options.content, cb) {
        let result = [];
        await getSlidersData.call(this, {
            content: data,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: this.options.ajax,
            hyphen: this.options.hyphen,
            parent: this.targetEl,
            wrap: this.wrapEl,
            lazy: this.options.lazy,
            tplStr: this.options.tplStr,
            tplEng: this.options.tplEng,
            cb: (resp) => {
                result = [...resp];
            }
        });
        cb && cb(result);
        return result;
    }
    findSlider(data) {
        let dataType = getDataType(data), result;
        if (dataType === 'Number') {
            result = this.sliders[data]?.slider;
        }
        else if (dataType === 'Object') {
            result = data?.slider;
        }
        else {
            result = getEl(data, this.wrapEl);
        }
        return result;
    }
    appendSliders(data = this.sliders, placement = 'end') {
        let target, sliders = data.map((k) => k.slider);
        if (placement === 'start') {
            sliders.reverse();
            for (let k of sliders) {
                this.wrapEl.insertAdjacentElement('afterbegin', k);
            }
        }
        else if (placement === 'end') {
            this.wrapEl.append(...sliders);
        }
        else {
            target = this.findSlider(placement);
            if (target) {
                sliders.reverse();
                for (let k of sliders) {
                    target.insertAdjacentElement('afterend', k);
                }
            }
        }
    }
    createWrap() {
        let firstChild = this.targetEl.firstElementChild;
        if (firstChild) {
            firstChild.classList.add(`${prefix}swipe-wrap`);
            this.wrapEl = firstChild;
        }
        else {
            this.wrapEl = createEl('ul', { class: `${prefix}swipe-wrap ${prefix}reset` }, '');
            this.targetEl.appendChild(this.wrapEl);
        }
        if (this.targetEl.querySelector(`.${prefix}swipe-nav`)) {
            this.navEl = this.targetEl.querySelector(`.${prefix}swipe-nav`);
        }
        else if (this.options.integrated) {
            this.navEl = createEl('div', { class: `${prefix}swipe-nav` }, '');
            this.targetEl.appendChild(this.navEl);
        }
    }
    fillWedgets() {
        this.getBullets();
        this.fillNav();
        this.fillPgn();
        this.fillTotal();
    }
    updateWedgets() {
        this.getBullets();
        this.updatePgn();
        this.updateTotal();
    }
    setSlidesObs() {
        this.slidersObs = new Observe(this.slidersTmp, {
            onCompleted: () => {
                this.updateGap();
                this.updateWedgets();
                this.scrollIns.setSnapped({ force: true });
            }
        });
        this.sliders = this.slidersObs.proxy;
    }
    setBulletsObs() {
        this.bulletsObs = new Observe(this.bulletsTmp, {
            deep: true,
            onSet: (resp) => {
                if (resp.key === 'active' && resp.value === true) {
                    resp.proxy.el.setAttribute('active', '');
                    this.bulletSeq = this.findPrevNextBullet(resp.proxy);
                    this.toBullet(resp.proxy);
                    this.toggleNavDisabled(resp.proxy);
                    this.numEl && (this.numEl.innerHTML = this.bullets.findIndex((k) => k === resp.proxy) + 1);
                }
            },
            onDeleted: (resp) => {
                if (resp.key === 'active') {
                    resp.target.el.removeAttribute('active');
                }
            },
            onCompleted: () => {
            }
        });
        this.bullets = this.bulletsObs.proxy;
    }
    findPrevNextBullet(data) {
        let item = this.findBullet(data);
        if (!item)
            return { prev: null, next: null };
        let idx = this.bullets.findIndex((k) => k === item), prev = this.bullets[idx - 1], next = this.bullets[idx + 1];
        if (!prev) {
            if (this.options.loop) ;
            else if (this.options.autoplay.enable) {
                prev = this.bullets.at(-1);
            }
        }
        if (!next) {
            if (this.options.loop) ;
            else if (this.options.autoplay.enable) {
                next = this.bullets[0];
            }
        }
        return { prev, next, current: item };
    }
    toggleNavDisabled(data) {
        if (this.options.loop || !this.options.nav.enable)
            return;
        if (this.bullets.length < 2) {
            this.prevEl.setAttribute('disabled', '');
            this.nextEl.setAttribute('disabled', '');
        }
        else {
            let item = this.findBullet(data);
            if (!item)
                return;
            if (item === this.bullets[0]) {
                this.prevEl.setAttribute('disabled', '');
                this.nextEl.removeAttribute('disabled');
            }
            else if (item === this.bullets.at(-1)) {
                this.nextEl.setAttribute('disabled', '');
                this.prevEl.removeAttribute('disabled');
            }
            else {
                this.prevEl.removeAttribute('disabled');
                this.nextEl.removeAttribute('disabled');
            }
        }
    }
    setSlideSize() {
        if (this.options.slides > 0) {
            this.sliders.forEach((k) => {
                this.setSingleSize(k);
            });
            this.updateGap();
        }
    }
    updateGap() {
        if (this.options.slides > 0) {
            if (this.options.divide > 1) {
                this.sliders.forEach((k, i) => {
                    this.setSingleGap(k, i);
                });
            }
            else {
                if (this.options.gap > 0) {
                    this.sliders.forEach((k) => {
                        this.setSingleGap(k);
                    });
                }
            }
        }
    }
    
    getIdxFromActive(active) {
        let result = this.getActiveIdex(active);
        return clampVal({ val: result, min: 0, max: this.sliders.length - 1 });
    }
    getActiveIdex(data) {
        let result = -1;
        if (typeof data === 'number') {
            result = data;
        }
        else {
            let tmp = getEl(data, this.targetEl);
            tmp && (result = this.sliders.findeIndex((k) => k.slider === tmp));
        }
        result >= this.sliders.length && (result = -1);
        return result;
    }
    getInitSlider() {
        if (this.sliders.length === 0 || this.actIdx === -1)
            return null;
        if (this.options.centered.enable && this.options.centered.fill) {
            if (this.options.slides > 1 && this.actIdx < ~~(this.options.slides / 2)) {
                this.actIdx = ~~(this.options.slides / 2);
            }
        }
        return this.sliders[this.actIdx].slider;
    }
    getAccuracy(value, higher = true) {
        let places = value.toString().split(".")[1] ? value.toString().split(".")[1].length : 0;
        return places > 3 ? toNumber(value + (higher ? 0.1 : 0), { mode: higher ? 'ceil' : 'floor', places: 1 }) : value;
    }
    getSingleSize() {
        if (!(this.options.slides > 0))
            return;
        this.slideSize = this.getAccuracy(this.scrollIns.childAvgSize, this.scrollIns.gap > 0 ? false : true);
        if (this.options.divide > 1) {
            let netValueDivide = this.scrollIns.reverseSize - this.scrollIns.gap * (this.options.divide - 1), singleValueDivide = netValueDivide / this.options.divide;
            this.slideSizeDivide = this.getAccuracy(singleValueDivide, this.scrollIns.gap > 0 ? false : true);
        }
    }
    setSingleSize(item) {
        if (!(this.options.slides > 0))
            return;
        item.slider.style[this.scrollIns.forwardMap.size] = this.slideSize + 'px';
    }
    setSingleGap(item, index) {
        if (!(this.options.slides > 0))
            return;
        let idx = isNull(index) ? this.sliders.findIndex((k) => k === item) : index;
        if (this.options.divide > 1) {
            item.slider.style[this.scrollIns.reverseMap.size] = this.getAccuracy(this.slideSizeDivide, false) + 'px';
            if (idx % this.options.divide !== 0 && this.scrollIns.gap) {
                item.slider.style[this.scrollIns.reverseMap.gap] = this.scrollIns.gap + 'px';
            }
            idx >= this.options.divide && (item.slider.style[this.scrollIns.forwardMap.gap] = this.scrollIns.gap + 'px');
        }
        else {
            idx !== 0 && (item.slider.style[this.scrollIns.forwardMap.gap] = this.scrollIns.gap + 'px');
        }
    }
    getBullets() {
        this.bullets.length = 0;
        let unsnappeds = [], len = this.sliders.length;
        if (this.options.slides > 0) {
            this.sliders.forEach((k, i) => {
                let obj = { target: k, el: createEl('li'), index: i };
                if (this.options.centered.enable) {
                    this.bullets.push(obj);
                }
                else {
                    if (this.options.loop) {
                        if (this.group && this.options.slides > 1) {
                            if (i % (this.options.slides * this.options.divide) === 0) {
                                this.bullets.push(obj);
                            }
                            else {
                                k.slider.setAttribute(this.options.unalign, '');
                            }
                        }
                        else {
                            this.bullets.push(obj);
                        }
                    }
                    else {
                        let num = this.options.divide * (this.group || this.options.slides);
                        if ((i % num === 0 && this.sliders.length - i >= num) || i === len - num) {
                            this.bullets.push(obj);
                        }
                        else {
                            if (this.group) {
                                unsnappeds.push(k.slider);
                                setSingleSel(k.slider, this.options.unalign);
                            }
                        }
                    }
                    this.updateUnsnappeds(unsnappeds);
                }
            });
        }
        else {
            if (this.scrollIns.isBaby) {
                this.bullets.push({ target: this.sliders[0], el: createEl('li'), index: 0 });
            }
            else {
                let splits = this.getSplits(), tmp = this.getSanpOffsetArr();
                for (let k of tmp) {
                    for (let v of splits) {
                        ((k.offsetStart <= v && k.offsetEnd > v)) && this.bullets.push({ target: k.target, el: createEl('li'), index: k.index });
                    }
                }
            }
        }
    }
    updateUnsnappeds(unsnappeds) {
        this.scrollIns.unsnappeds = unique([...this.scrollIns.unsnappeds, ...unsnappeds]);
        this.scrollIns.snappeds = [...this.wrapEl.children].filter(k => !this.scrollIns.unsnappeds.includes(k));
    }
    getSplits() {
        let num = Math.floor(this.scrollIns.wrapSize / this.scrollIns.baseSize), getSplits = (num, unit) => {
            let result = [];
            for (let k = 0; k < num; k++) {
                result.push(k * unit);
            }
            return result;
        }, result = getSplits(num, this.scrollIns.baseSize);
        result.push(this.scrollIns.wrapSize - this.scrollIns.baseSize);
        return result;
    }
    getSanpOffsetArr() {
        let result = [];
        this.sliders.forEach((k, i) => {
            if (this.scrollIns.snappeds.includes(k.slider)) {
                let idx = this.scrollIns.snappeds.findIndex((v) => v === k.slider), nextSnapped = this.scrollIns.snappeds[idx + 1], offsetStart = k.slider[this.scrollIns.forwardMap.offset], offsetEnd = nextSnapped ? this.scrollIns.snappeds[idx + 1][this.scrollIns.forwardMap.offset] : offsetStart + k.slider[this.scrollIns.forwardMap.outer];
                result.push({ offsetStart, offsetEnd, target: k, index: i });
            }
        });
        return result;
    }
    updateTotal(value = this.getTotal()) {
        if (!this.options.total.enable)
            return false;
        if (!value.bullets) {
            this.numEl.innerHTML = this.denomEl.innerHTML = 0;
        }
        else {
            this.numEl.innerHTML = value.curBullet + 1;
            this.denomEl.innerHTML = value.bullets;
        }
    }
    fillTotal() {
        if (!this.options.total.enable)
            return false;
        let total = this.getTotal();
        this.totalEl = createEl('span', { class: `${prefix}swipe-total` }, `<i></i><s>${this.options.total.hyphen}</s><u></u>`);
        this.numEl = this.totalEl.querySelector('i');
        this.denomEl = this.totalEl.querySelector('u');
        this.updateTotal(total);
        this.options.total.placement && this.totalEl.setAttribute('placement', this.options.total.placement);
        this.targetEl.appendChild(this.totalEl);
    }
    getTotal() {
        return { curSlider: this.actIdx, sliders: this.sliders.length, curBullet: this.bullets.findIndex((k) => k.active), bullets: this.bullets.length };
    }
    fillNav() {
        if (!this.options.nav.enable)
            return false;
        let addNav = (type) => {
            let result = null, text = (type === 'prev') ? (this.options.axis === 'x' ? 'left' : 'up') :
                (type === 'next') ? (this.options.axis === 'x' ? 'right' : 'down') : '', className = `${prefix}swipe-${type}`, el = this.targetEl.querySelector(`.${className}`);
            if (el) {
                result = el;
            }
            else if (this.options.nav[type].selector) {
                result = getEl(this.options.nav[type].selector);
            }
            else {
                result = createEl('i', { class: className }, text);
                this.navEl ? this.navEl.appendChild(result) : this.targetEl.appendChild(result);
            }
            if (result) {
                this.options.nav[type].classes && classes(result).add(this.options.nav[type].classes);
                this.options.nav[type].text && (result.innerHTML = this.options.nav[type].text);
                this.options.nav.classes && classes(result).add(this.options.nav.classes);
                this.options.nav.fill && result.setAttribute('filled', '');
            }
            return result;
        };
        if (this.options.nav.enable) {
            this.prevEl = addNav('prev');
            this.nextEl = addNav('next');
            if (this.prevEl) {
                this.prevEl.onclick = throttle(() => {
                    this.toPrevBullet();
                }, { intvl: this.options.nav.intvl, prevent: true });
            }
            if (this.nextEl) {
                this.nextEl.onclick = throttle(() => {
                    this.toNextBullet();
                }, { intvl: this.options.nav.intvl, prevent: true });
            }
        }
    }
    fillPgn() {
        if (!this.options.pgn.enable)
            return false;
        this.pgnIns = null;
        let className = `${prefix}swipe-pgn`, pgnTmp = [...this.targetEl.children].find((k) => k.classList.contains(className));
        if (pgnTmp) {
            this.pgnEl = pgnTmp;
        }
        else if (this.options.pgn.selector) {
            let selType = getDataType(this.options.pgn.selector);
            if (selType.includes('HTML')) {
                this.pgnEl = this.options.pgn.selector;
            }
            else if (selType === 'String') {
                if (getSelectorType(this.options.pgn.selector)) {
                    this.pgnEl = getEl(this.options.pgn.selector);
                }
                else {
                    this.pgnIns = instance.find(this.options.pgn.selector, 'swipe');
                    this.pgnIns && (this.pgnEl = this.pgnIns.wrapEl);
                }
            }
            else if (selType === 'Object') {
                this.pgnIns = this.options.pgn.selector;
                this.pgnEl = this.pgnIns.wrapEl;
            }
        }
        else {
            this.pgnEl = createEl('ul', { class: `${className} ${prefix}reset` }, '');
            this.navEl && this.prevEl ? this.prevEl.insertAdjacentElement('afterend', this.pgnEl) : this.targetEl.appendChild(this.pgnEl);
        }
        if (this.pgnEl) {
            this.options.pgn.type && this.pgnEl.setAttribute('type', this.options.pgn.type);
            this.options.pgn.classes && classes(this.pgnEl).add(this.options.pgn.classes);
            if (this.pgnEl.children.length) {
                this.bullets.forEach((k, i) => {
                    if (this.pgnEl.children[i]) {
                        k.el = this.pgnEl.children[i];
                        i === this.actIdx && this.setBulletActive(k);
                    }
                });
                this.setBulletAction();
            }
            else {
                this.updatePgn();
            }
        }
    }
    setBulletAction() {
        this.bullets.forEach((k) => {
            k.el.onclick = () => {
                this.setBulletActive(k);
                this.scrollIns.scrollTo({ target: k.target.slider, snap: true });
            };
        });
    }
    updatePgn() {
        if (!this.options.pgn.enable)
            return;
        this.pgnEl.innerHTML = '';
        let len = this.bullets.length, fragment = document.createDocumentFragment();
        this.bullets.forEach((k, i) => {
            i === this.actIdx && this.setBulletActive(k);
            let html;
            switch (this.options.pgn.type) {
                case 'text':
                    html = k.target.source.label || this.options.pgn.data[i];
                    break;
                case 'thumb':
                    html = `<img src="${k.target.source.thumb || this.options.pgn.data[i]}"/>`;
                    break;
                case 'index':
                    html = i + 1;
                    break;
                default:
                    html = '';
            }
            if (this.options.pgn.before) {
                this.options.pgn.before.call(this, { index: i, total: len, html, bullet: k, parent: this.pgnEl });
            }
            else {
                k.el.innerHTML = html;
            }
            fragment.appendChild(k.el);
        });
        this.pgnEl.appendChild(fragment);
        this.options.pgn.after && this.options.pgn.after.call(this, { total: len, parent: this.pgnEl });
        this.setBulletAction();
    }
    findNavSnap(type = 'next') {
        let index = this.scrollIns.snappeds.findIndex((k) => k === this.scrollIns.snapped), target = null;
        if (index < 0)
            return target;
        if (type === 'next') {
            if (this.options.loop && index === this.scrollIns.snappeds.length - 1) {
                target = this.scrollIns.snappeds[0];
            }
            else {
                index !== this.scrollIns.snappeds.length - 1 && (target = this.scrollIns.snappeds[index + 1]);
            }
        }
        else if (type === 'prev') {
            if (this.options.loop && index === 0) {
                target = this.scrollIns.snappeds.at(-1);
            }
            else {
                index !== 0 && (target = this.scrollIns.snappeds[index - 1]);
            }
        }
        else if (type === 'start') {
            target = this.scrollIns.snappeds[0];
        }
        else if (type === 'end') {
            target = this.scrollIns.snappeds.at(-1);
        }
        return target;
    }
    findSnap(data) {
        let dataType = getDataType(data), result;
        if (dataType === 'Number') {
            let tmp = this.sliders[data];
            result = tmp && this.scrollIns.snappeds.includes(tmp.slider) ? tmp : null;
        }
        else if (dataType.includes('HTML') || dataType === 'String') {
            let tmp = getEl(data, this.wrapEl);
            result = tmp && this.scrollIns.snappeds.includes(tmp) ? tmp : null;
        }
        else if (dataType === 'Object' && data.hasOwnProperty('slider')) {
            result = this.scrollIns.snappeds.includes(data.slider) ? data : null;
        }
        return result;
    }
    findBullet(data) {
        let dataType = getDataType(data), result;
        if (dataType === 'Number') {
            result = this.bullets[data];
        }
        else if (dataType.includes('HTML') || dataType === 'String') {
            result = this.bullets.find((k) => k.target.slider === getEl(data, this.wrapEl));
        }
        else if (dataType === 'Object' && data.hasOwnProperty('el')) {
            result = data;
        }
        return result;
    }
    toBullet(data, opt = {}) {
        if (this.destroyed)
            return;
        let bullet = this.findBullet(data);
        if (!bullet || this.bulletSeq.current === bullet)
            return;
        this.scrollIns.scrollTo({
            target: bullet.target.slider,
            snap: true,
            before: () => {
                this.setBulletActive(bullet);
                opt.before && opt.before.call(this, bullet);
            },
            after: () => {
                opt.after && opt.after.call(this, bullet);
            }
        });
    }
    toPrevBullet(cb) {
        if (this.destroyed)
            return;
        this.bulletSeq.prev && this.toBullet(this.bulletSeq.prev, {
            after: () => {
                cb && cb.call(this, this.bulletSeq.prev);
            }
        });
    }
    toNextBullet(cb) {
        if (this.destroyed)
            return;
        this.bulletSeq.next && this.toBullet(this.bulletSeq.next, {
            after: () => {
                cb && cb.call(this, this.bulletSeq.next);
            }
        });
    }
    toSnap(data, opt = {}) {
        if (this.destroyed)
            return;
        let target = this.findSnap(data);
        if (!target || this.scrollIns.snapped === target)
            return;
        this.scrollIns.scrollTo({
            target,
            snap: true,
            before: () => {
                opt.before && opt.before.call(this, target);
            },
            after: () => {
                opt.after && opt.after.call(this, target);
            }
        });
    }
    toPrevSnap(cb) {
        if (this.destroyed)
            return;
        let target = this.findNavSnap('prev');
        if (!target)
            return;
        this.scrollIns.scrollTo({
            target,
            snap: true,
            after: () => {
                cb && cb.call(this, target);
            }
        });
    }
    toNextSnap(cb) {
        if (this.destroyed)
            return;
        if (this.destroyed)
            return;
        let target = this.findNavSnap('next');
        if (!target)
            return;
        this.scrollIns.scrollTo({
            target,
            snap: true,
            after: () => {
                cb && cb.call(this, target);
            }
        });
    }
    toStart(cb) {
        if (this.destroyed)
            return;
        let item = this.bullets[0];
        if (item.active)
            return;
        this.toBullet(item, {
            after: (bullet) => {
                cb && cb.call(this, bullet);
            }
        });
    }
    toEnd(cb) {
        if (this.destroyed)
            return;
        let item = this.bullets.at(-1);
        if (item.active)
            return;
        this.toBullet(item, {
            after: (bullet) => {
                cb && cb.call(this, bullet);
            }
        });
    }
    setLoopPlay() {
        if (this.options.loop === false) {
            return false;
        }
        if (this.options.slides === 'auto') {
            this.startClone = this.slides.map((k) => k.dom.cloneNode(true));
            this.endClone = this.slides.map((k) => k.dom.cloneNode(true));
            this.totalClone = this.slides.length;
        }
        else {
            this.startClone = this.slides.slice(0, this.options.slides).map((k) => k.dom.cloneNode(true));
            this.endClone = this.slides.slice(this.slides.length - this.options.slides).map((k) => k.dom.cloneNode(true));
            this.totalClone = this.options.slides;
        }
        this.scrollIns.add(this.endClone, { placement: 'start' });
        this.scrollIns.add(this.startClone, { placement: 'end' });
    }
    startCountdown() {
        if (!this.countdownIns)
            return;
        this.countdownIns.setCompleted();
        this.countdownIns.animateTo(0, { duration: this.options.autoplay.delay });
    }
    setAutoPlay() {
        if (!this.options.autoplay.enable)
            return;
        if (this.options.autoplay.countdown) {
            if (!this.countdownIns) {
                this.countdownIns = new Progress(this.countdownEl, extend({
                    target: {
                        label: false,
                        type: 'circle',
                        curve: 'linear',
                        size: 'xxs',
                        control: true,
                        duration: this.options.autoplay.delay,
                        onPause: () => {
                            this.pause();
                        },
                        onContinue: (resp) => {
                            this.continue(resp.durRest);
                        }
                    },
                    source: this.options.countdown,
                }));
            }
            else {
                this.countdownIns.init();
            }
            this.targetEl.appendChild(this.countdownEl);
        }
        this.play();
    }
    continue(duration) {
        delay({
            duration,
            done: () => {
                this.toNextBullet(() => {
                    this.play();
                });
            }
        });
    }
    play(delay, cb) {
        if (this.destroyed || !this.paused)
            return;
        if (!this.options.autoplay.enable)
            return;
        this.interval && clearInterval(this.interval);
        this.interval = setInterval(this.intervalEvt, delay || this.options.autoplay.delay);
        this.startCountdown();
        this.paused = false;
        super.listen({ name: 'play', cb });
        return this;
    }
    pause(clear = false, cb) {
        if (this.destroyed || this.paused)
            return;
        if (!this.options.autoplay.enable)
            return;
        clearInterval(this.interval);
        if (clear && this.countdownIns) {
            this.countdownEl.remove();
            this.countdownIns.destroy();
        }
        this.paused = true;
        super.listen({ name: 'pause', cb });
        return this;
    }
    async add(data, options) {
        if (this.destroyed || isEmpty(data))
            return;
        let tmp, opts = Object.assign({ placement: 'end' }, options);
        await this.getSliders(data, (resp) => {
            tmp = resp;
            this.sliders.push(...tmp);
        });
        this.appendSliders(tmp, opts.placement);
        super.listen({ name: 'add', cb: opts.cb, params: [tmp] });
        return { ins: this, data: tmp };
    }
    remove(data = 'end', cb) {
        if (this.destroyed || isEmpty(data))
            return;
        let tmp;
        if (data === 'start') {
            tmp = this.sliders[0]?.slider;
        }
        else if (data === 'end') {
            tmp = this.sliders.at(-1)?.slider;
        }
        else {
            tmp = this.findSlider(data);
        }
        if (!tmp)
            return;
        tmp.remove();
        super.listen({ name: 'remove', cb, params: [this.sliders.find((k) => k.slider === tmp)] });
        return this;
    }
    destroy(cb) {
        if (this.destroyed)
            return this;
        for (let k of this.sliders) {
            k.spy && k.spy.destroy();
        }
        this.scrollIns.destroy();
        this.spyIns && this.spyIns.destroy();
        this.countdownIns && this.countdownIns.destroy();
        window.removeEventListener('keydown', this.keyToSwipe);
        this.bullets.forEach((k) => {
            k.el.onclick = null;
        });
        this.prevEl && (this.prevEl.onclick = null);
        this.nextEl && (this.nextEl.onclick = null);
        if (this.toolsEl) {
            this.options.tools.children.forEach((k) => {
                k.el.onclick = null;
            });
        }
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
    updateCont(text, cb) {
        if (this.destroyed) {
            return this;
        }
        this.options.content = text;
        this.init();
        super.updateCache({ content: this.content });
        super.listen({ name: 'updatedCont', cb, params: [this.content] });
        return this;
    }
}

let AXTMP_hyphen$1 = config.splitHyphen;
const optLazy = [
    {
        attr: 'root',
        prop: 'root',
        value: null
    },
    {
        attr: 'content',
        prop: 'content',
        value: ''
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'hyphen',
        prop: 'hyphen',
        value: AXTMP_hyphen$1,
    },
    {
        attr: 'tpl-str',
        prop: 'tplStr',
        value: '',
    },
    {
        attr: 'tpl-eng',
        prop: 'tplEng',
        value: null,
    },
    {
        attr: 'type',
        prop: 'type',
        value: 'src',
    },
    {
        attr: 'spy',
        prop: 'spy',
        value: {},
    },
    {
        attr: 'on-shown',
        prop: 'onShown',
        value: null,
    },
    ...optBase
];

const getImgBlank = () => getComputedVar(`--${prefix}blank`).split('"')[1];

class Lazy extends ModBaseListenCache {
    nodeName;
    resType;
    spyIns;
    removeAttrFn;
    options = {};
    static hostType = 'node';
    static optMaps = optLazy;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            maps: Lazy.optMaps,
            host: elem,
        });
        this.nodeName = this.targetEl.nodeName;
        this.removeAttrFn = () => {
            this.targetEl.removeAttribute('lazy-src');
            super.listen({ name: 'shown', params: [this.targetEl] });
        };
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.resType = this.options.type || (this.targetEl.hasAttribute('src') ? 'src' : 'async');
        if (this.resType === 'src') {
            this.targetEl.src = getImgBlank();
            !this.targetEl.hasAttribute('lazy-src') && this.targetEl.setAttribute('lazy-src', '');
        }
        this.spyIns = new Spy(this.targetEl, extend({
            target: {
                root: this.options.root,
                repeat: false,
                onShown: async () => {
                    super.listen({ name: 'showing', params: [this.targetEl] });
                    if (this.resType === 'src') {
                        this.targetEl.src = this.options.content;
                        if (['VIDEO', 'AUDIO'].includes(this.nodeName)) {
                            this.targetEl.onloadeddata = this.removeAttrFn;
                        }
                        else if (['IMG', 'IFRAME'].includes(this.nodeName)) {
                            this.targetEl.onload = this.removeAttrFn;
                        }
                        else {
                            this.removeAttrFn();
                        }
                    }
                    else if (this.resType === 'async') {
                        await getContent.call(this, {
                            content: this.options.content,
                            contType: this.options.contType || 'async',
                            contData: this.options.contData,
                            ajax: Object.assign({ target: this.targetEl }, this.options.ajax),
                            hyphen: this.options.hyphen,
                            cb: (data) => {
                                setContent({
                                    target: this.targetEl,
                                    content: data,
                                    template: this.options.tplStr,
                                    engine: this.options.tplEng,
                                });
                                this.targetEl.removeAttribute('lazy-async');
                                this.targetEl.removeAttribute('lazy-type');
                                this.targetEl.removeAttribute('lazy-data');
                                this.targetEl.removeAttribute('lazy-tpl');
                                super.listen({ name: 'shown', params: [this.targetEl] });
                            }
                        });
                    }
                },
            },
            source: this.options.spy
        }));
        super.listen({ name: 'initiated', cb });
        return this;
    }
    destroy(cb) {
        if (this.destroyed)
            return this;
        this.spyIns.destroy();
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

const optInfinite = [
    {
        attr: 'trigger',
        prop: 'trigger',
        value: 'scroll',
    },
    {
        attr: 'spy',
        prop: 'spy',
        value: {
            enable: true,
        },
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'spin',
        prop: 'spin',
        value: '<ax-spin></ax-spin>',
    },
    {
        attr: 'max',
        prop: 'max',
        value: 20,
    },
    {
        attr: 'content',
        prop: 'content',
        value: ''
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'tpl-str',
        prop: 'tplStr',
        value: '',
    },
    {
        attr: 'tpl-eng',
        prop: 'tplEng',
        value: null,
    },
    {
        attr: 'media',
        prop: 'media',
        value: {
            caption: '',
            brief: '',
        },
    },
    {
        attr: 'b4-append',
        prop: 'b4Append',
        value: null,
    },
    {
        attr: 'b4-remove',
        prop: 'b4Remove',
        value: null,
    },
    {
        attr: 'b4-clear',
        prop: 'b4Clear',
        value: null,
    },
    {
        attr: 'on-append',
        prop: 'onAppend',
        value: null,
    },
    {
        attr: 'on-finish',
        prop: 'onFinish',
        value: null,
    },
    {
        attr: 'on-error',
        prop: 'onError',
        value: null,
    },
    ...optBase
];

class Infinite extends ModBaseListenCache {
    options = {};
    content;
    index;
    contReq;
    contXhr;
    statusEl;
    spinEl;
    nextEl;
    nextBtn;
    tipsEl;
    listSpyIns;
    statusSpyIns;
    static hostType = 'node';
    static optMaps = optInfinite;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            maps: Infinite.optMaps,
            host: elem,
            component: false,
            spread: ['spy']
        });
        this.content = [];
        this.index = 0;
        this.contReq = {
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: {
                error: (resp) => {
                    this.finish(true);
                    super.listen({ name: 'error', params: [resp] });
                },
                xhrName: 'contXhr',
                ...this.options.ajax,
            },
        };
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.setAttrs();
        this.useTpl();
        this.getStatusEl();
        this.targetEl.appendChild(this.statusEl);
        await this.getInitContent();
        this.options.spy.enable && this.getListSpyIns();
        if (this.options.trigger === 'scroll') {
            this.getStatusSpyIns();
        }
        else if (this.options.trigger === 'click') {
            this.nextBtn.onclick = () => {
                this.getStatusSpyIns();
            };
        }
        else if (this.options.trigger === 'clicks') {
            this.nextBtn.onclick = () => {
                this.renderList(this.index);
            };
        }
        super.listen({ name: 'initiated', cb });
        return this;
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}inf`);
        classes(this.targetEl).add(this.options.classes);
        this.targetEl.setAttribute('trigger', this.options.trigger);
    }
    finish(err = false) {
        this.statusEl.setAttribute('status', 'finish');
        this.tipsEl.innerHTML = err ? this.options.lang.error : this.options.lang.finish;
        this.statusSpyIns && this.statusSpyIns.destroy();
        super.listen({ name: 'finish' });
    }
    getListSpyIns() {
        this.listSpyIns = new Spy(null, extend({
            target: {
                repeat: false,
                visible: false,
                in: `${prefix}fadeIn`,
            },
            source: this.options.spy,
        }));
    }
    getStatusSpyIns() {
        this.statusSpyIns = new Spy(this.statusEl, {
            root: this.targetEl,
            onIn: debounce(() => {
                this.renderList(this.index);
            })
        });
    }
    async renderList(index) {
        if (this.destroyed || index >= this.content.length)
            return;
        this.statusEl.setAttribute('status', 'loading');
        this.tipsEl.innerHTML = this.options.lang.loading;
        await this.getPageData(this.content[index], async (data) => {
            this.options.b4Append && await this.options.b4Append.call(this, data);
            this.statusEl.setAttribute('status', 'loaded');
            this.tipsEl.innerHTML = this.options.lang.loaded;
            let fragment = document.createDocumentFragment();
            for (let k of data) {
                k.classList.add(`${prefix}inf-item`);
                fragment.appendChild(k);
            }
            this.targetEl.insertBefore(fragment, this.statusEl);
            this.listSpyIns && this.listSpyIns.add(data);
            super.listen({ name: 'append', params: [data] });
            this.index++;
            (this.index >= this.content.length) && this.finish();
        });
    }
    getStatusEl() {
        this.statusEl = this.targetEl.querySelector(`.${prefix}inf-status`) || createEl('div', { class: `${prefix}inf-status`, status: 'preload' });
        this.spinEl = createEl('div', { [alias]: 'spin' }, this.options.spin);
        this.statusEl.appendChild(this.spinEl);
        let nextTmp = this.targetEl.querySelector(`[${alias}="next"]`);
        if (nextTmp) {
            this.nextEl = nextTmp;
        }
        else {
            this.nextEl = createEl('div', { [alias]: 'next' }, this.options.lang.next);
            ['click', 'clicks'].includes(this.options.trigger) && this.statusEl.appendChild(this.nextEl);
        }
        this.nextBtn = this.nextEl.firstElementChild;
        let tipsTmp = this.targetEl.querySelector(`[${alias}="tips"]`);
        if (tipsTmp) {
            this.tipsEl = tipsTmp;
        }
        else {
            this.tipsEl = createEl('div', { [alias]: 'tips' }, this.options.lang.preload);
            this.statusEl.appendChild(this.tipsEl);
        }
    }
    async getInitContent() {
        let result = [];
        if (this.options.content) {
            let contType = getDataType(this.options.content);
            if (contType === 'String') {
                result = parseUrlArr(this.options.content, { max: this.options.max });
            }
            else if (contType === 'Array' && this.options.content.length) {
                let firstType = getDataType(this.options.content[0]);
                if (firstType === 'Object') {
                    for (let k of this.options.content) {
                        let tmp = extend({ target: deepClone(this.contReq), source: k });
                        result.push(await getContent.call(this, tmp));
                    }
                }
                else if (firstType === 'Array') {
                    for (let k of this.options.content) {
                        if (!k.length)
                            continue;
                        let sonType = getDataType(k[0]);
                        if (sonType.includes('HTML')) {
                            result = [...this.options.content];
                        }
                        else if (sonType === 'Object') {
                            let tmp = k.map((k) => tplToEl(super.getTplcont(k)));
                            result.push(tmp);
                        }
                    }
                }
                else {
                    result = this.options.content;
                }
            }
        }
        else {
            let pages = getEls(`[${alias}="page"]`, this.statusEl);
            result = pages.map((k) => k.href).filter(Boolean);
        }
        this.content = result.filter((k) => !isEmpty(k));
    }
    async getPageData(data, cb) {
        if (this.destroyed)
            return;
        let dataType = getDataType(data), result = [], contReq = deepClone(this.contReq);
        if (dataType === 'String') {
            result = this.options.contType === 'text' ? tplToEls(data) : await getContent.call(this, { content: data, ...contReq });
        }
        if (dataType.includes('HTML')) {
            result = [...data.children];
        }
        else if (dataType === 'Object') {
            let params = extend({
                target: contReq,
                source: data
            }), tmp;
            tmp = await getContent.call(this, params);
            if (isEmpty(tmp)) {
                if (typeof tmp === 'string') {
                    result = tplToEls(data);
                }
                else if (Array.isArray(tmp) && getDataType(tmp[0].include('HTML'))) {
                    result = tmp;
                }
            }
        }
        else if (dataType === 'Array') {
            result = this.arr2Nodes(data);
        }
        let resultType = getDataType(result);
        if (resultType === 'String') {
            result = tplToEls(result);
        }
        else if (resultType === 'Array') {
            result = this.arr2Nodes(result);
        }
        result = result.filter(Boolean);
        cb && cb(result);
        return result;
    }
    arr2Nodes(data) {
        let result = [];
        if (!data.length)
            return result;
        let itemType = getDataType(data[0]);
        if (itemType.includes('HTML')) {
            result = data;
        }
        else if (itemType === 'Object') {
            result = data.map((k) => tplToEl(super.getTplcont(k)));
        }
        return result;
    }
    
    destroy(cb) {
        if (this.destroyed)
            return this;
        this.nextBtn && (this.nextBtn.onclick = null);
        this.listSpyIns && this.listSpyIns.destroy();
        this.statusSpyIns && this.statusSpyIns.destroy();
        this.contXhr && this.contXhr.abort();
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

const optVirtualize = [
    {
        attr: 'axis',
        prop: 'axis',
        value: 'y',
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'size',
        prop: 'size',
        value: 0,
    },
    {
        attr: 'spill',
        prop: 'spill',
        value: 2,
    },
    {
        attr: 'names',
        prop: 'names',
        value: {
            wrap: 'div',
            list: 'div',
            cont: 'div',
            item: 'section'
        },
    },
    {
        attr: 'index',
        prop: 'index',
        value: 0,
    },
    {
        attr: 'dynamic',
        prop: 'dynamic',
        value: true,
    },
    {
        attr: 'content',
        prop: 'content',
        value: ''
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'tpl-str',
        prop: 'tplStr',
        value: '',
    },
    {
        attr: 'tpl-eng',
        prop: 'tplEng',
        value: null,
    },
    {
        attr: 'b4-append',
        prop: 'b4Append',
        value: null,
    },
    {
        attr: 'b4-updateitem',
        prop: 'b4UpdateItem',
        value: null,
    },
    {
        attr: 'b4-clear',
        prop: 'b4Clear',
        value: null,
    },
    {
        attr: 'on-append',
        prop: 'onAppend',
        value: null,
    },
    {
        attr: 'on-getcont',
        prop: 'onGetCont',
        value: null,
    },
    {
        attr: 'on-exhausted',
        prop: 'onExhausted',
        value: null,
    },
    {
        attr: 'on-tostart',
        prop: 'onToStart',
        value: null,
    },
    {
        attr: 'on-toend',
        prop: 'onToEnd',
        value: null,
    },
    {
        attr: 'on-updateItem',
        prop: 'onUpdatedItem',
        value: null,
    },
    {
        attr: 'on-rendered',
        prop: 'onRendered',
        value: null,
    },
    {
        attr: 'on-error',
        prop: 'onError',
        value: null,
    },
    ...optBase
];

class Virtualize extends ModBaseListenCache {
    options = {};
    propsMap;
    contReq;
    autoRenderEvt;
    obsRenderEvt;
    avgSize;
    targetSize;
    wrapEl;
    contEl;
    listEl;
    startSpaceEl;
    endSpaceEl;
    sizesObs;
    itemSizes;
    startSpyIns;
    endSpyIns;
    itemObs;
    targetObs;
    listSize;
    paddingStart;
    startIdx;
    endIdx;
    offsetVal;
    scrollVal;
    nodes;
    paddingEnd;
    wrapSize;
    contXhr;
    content;
    static hostType = 'node';
    static optMaps = optVirtualize;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            maps: Virtualize.optMaps,
            host: elem,
            component: false,
            spread: []
        });
        this.propsMap = propsMap[this.options.axis];
        this.contReq = {
            content: this.options.content,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: {
                spinSel: this.targetEl,
                error: (resp) => {
                    super.listen({ name: 'error', params: [resp] });
                },
                xhrName: 'contXhr',
                ...this.options.ajax,
            },
        };
        this.autoRenderEvt = () => {
            this.renderFromStart();
        };
        this.obsRenderEvt = debounce(() => {
            this.initialized && this.renderFromStart();
        });
        this.avgSize = Math.max(parseInt(style(document.body).lineHeight), toPixel(this.options.size));
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.setAttrs();
        this.targetSize = this.targetEl[this.propsMap.inner];
        this.useTpl();
        this.getNodesTree();
        await this.getInitData();
        this.setSizesObs();
        this.setSpy();
        this.targetEl.addEventListener("scroll", this.autoRenderEvt, false);
        this.itemObs = new MutationObserver(this.obsRenderEvt);
        this.targetObs = new ResizeObserver(this.obsRenderEvt);
        this.targetObs.observe(this.targetEl);
        this.renderFromStart(this.options.index);
        super.listen({ name: 'initiated', cb });
        return this;
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}virt`);
        this.targetEl.setAttribute('axis', this.options.axis);
        classes(this.targetEl).add(this.options.classes);
    }
    getAvgArr(len) {
        return Array(len).fill(null).map(() => { return { size: this.avgSize, rendered: false }; });
    }
    setSizesObs() {
        this.sizesObs = new Observe(this.getAvgArr(this.content.length), {
            deep: true,
            onCompleted: () => {
                if (this.itemSizes.findIndex((k) => !k.rendered) < 0) {
                    super.listen({ name: 'exhausted', params: [this.content] });
                }
            }
        });
        this.itemSizes = this.sizesObs.proxy;
    }
    setSpy() {
        this.startSpyIns = new Spy(this.startSpaceEl, {
            onIn: () => {
                super.listen({ name: 'toStart' });
            },
        });
        this.endSpyIns = new Spy(this.endSpaceEl, {
            onIn: () => {
                super.listen({ name: 'toEnd' });
            },
        });
    }
    setNodeMutation(el) {
        this.itemObs.observe(el, {
            childList: true,
            subtree: true,
            attributes: true,
        });
    }
    getNodesTree() {
        let tmpWrap = this.targetEl.querySelector(`.${prefix}virt-wrap`);
        if (tmpWrap) {
            this.wrapEl = tmpWrap;
        }
        else {
            this.wrapEl = createEl(this.options.names.wrap, { class: `${prefix}virt-wrap` });
            this.targetEl.appendChild(this.wrapEl);
        }
        let tmpCont = this.wrapEl.querySelector(`.${prefix}virt-cont`);
        if (tmpCont) {
            this.contEl = tmpWrap;
        }
        else {
            this.contEl = createEl(this.options.names.cont, { class: `${prefix}virt-cont` });
            this.wrapEl.appendChild(this.contEl);
        }
        let tmpList = this.contEl.querySelector(`.${prefix}virt-list`);
        if (tmpList) {
            this.wrapEl = tmpList;
        }
        else {
            this.listEl = createEl(this.options.names.list, { class: `${prefix}virt-list` });
            this.contEl.appendChild(this.listEl);
        }
        this.startSpaceEl = createEl('div', { class: `${prefix}virt-space` }, `${this.options.lang.preload}`);
        this.endSpaceEl = createEl('div', { class: `${prefix}virt-space` }, `${this.options.lang.preload}`);
        this.contEl.insertAdjacentElement('afterbegin', this.startSpaceEl);
        this.contEl.appendChild(this.endSpaceEl);
    }
    getWrapSize() {
        return this.options.dynamic ? this.itemSizes.reduce((sum, cur) => sum + cur.size, 0) : this.avgSize * this.itemSizes.length;
    }
    get2SetWrapSize() {
        this.wrapSize = this.getWrapSize();
        this.wrapEl.style[this.propsMap.size] = this.wrapSize + "px";
    }
    async getInitData() {
        let resp = await this.getData();
        this.content = resp.content;
    }
    async getData(data, cb) {
        let resp = await getContent.call(this, data ? Object.assign(this.contReq, data) : this.contReq), respType = getDataType(resp), result = { content: [], source: resp };
        this.respSource = resp;
        if (respType === 'Array') {
            result.content = resp;
        }
        else if (respType === 'Object' && resp.hasOwnProperty('data')) {
            result.content = (Array.isArray(resp.data)) ? resp.data : (resp?.data?.list || []);
        }
        else if (respType === 'String') {
            result.content = tplToEls(resp);
        }
        super.listen({ name: 'getCont', cb, params: [result] });
        return result;
    }
    getItemNode(data) {
        let dataType = getDataType(data), result;
        if (dataType.includes('HTML')) {
            result = data;
        }
        else {
            result = createEl(this.options.names.item, {});
            result.innerHTML = dataType === 'Object' ? super.getTplcont(data) : data.toString();
        }
        this.setNodeMutation(result);
        return result;
    }
    renderFromStart(start) {
        if (this.destroyed)
            return;
        this.listEl.innerHTML = '';
        this.listSize = 0;
        this.paddingStart = 0;
        this.startIdx = 0;
        this.endIdx = this.itemSizes.length - 1;
        this.offsetVal = 0;
        this.scrollVal = this.targetEl[this.propsMap.scroll];
        this.nodes = [];
        if (start) {
            this.startIdx = start - 1;
        }
        else {
            if (this.scrollVal) {
                for (let i = 0; i < this.itemSizes.length; i++) {
                    this.paddingStart += this.itemSizes[i].size;
                    if (this.paddingStart > this.scrollVal) {
                        this.startIdx = i;
                        break;
                    }
                    else if (i === this.itemSizes.length - 1 && this.paddingStart <= this.scrollVal) {
                        return;
                    }
                }
            }
        }
        this.paddingEnd = 0;
        for (let i = this.startIdx; i < this.itemSizes.length; i++) {
            this.paddingEnd += this.itemSizes[i].size;
            if (this.paddingEnd > this.targetSize) {
                this.endIdx = i;
                break;
            }
        }
        this.compensate();
        for (let i = this.startIdx; i <= this.endIdx; i++) {
            this.appendUpdateSize(i, 'beforeend', (size) => {
                this.listSize += size;
            });
        }
        let fillObj = {};
        this.fillBlank(this.listSize, (val) => {
            fillObj = val;
        });
        this.get2SetWrapSize();
        if (start) {
            this.updateScrollVal(start + (fillObj?.indexDiff || 0));
        }
        else {
            fillObj?.padding && (this.paddingStart -= fillObj.padding);
        }
        this.get2SetContOffset();
        super.listen({ name: 'rendered', params: [{ startIdx: this.startIdx, endIdx: this.endIdx, nodes: this.nodes, scrollVal: this.scrollVal, offsetVal: this.offsetVal, wrapSize: this.wrapSize, listSize: this.listSize }] });
    }
    fillBlank(total = this.listSize, cb) {
        let obj = { startIdx: 0, indexDiff: 0, padding: 0 };
        if (total < this.targetSize) {
            for (let i = 1; i < this.content.length; i++) {
                let index = this.startIdx - i;
                if (index < 0)
                    break;
                this.appendUpdateSize(index, 'afterbegin', (size) => {
                    total += size;
                    obj.padding += size;
                });
                if (total >= this.targetSize) {
                    obj.startIdx = index;
                    obj.indexDiff = index - this.startIdx;
                    this.startIdx = index;
                    this.listSize = total;
                    break;
                }
            }
            cb && cb(obj);
        }
    }
    appendUpdateSize(index, dir = 'afterbegin', cb) {
        if (index < 0 || index >= this.content.length)
            return;
        let itemEl = this.getItemNode(this.content[index]);
        this.nodes.push({ el: itemEl, index });
        this.listEl.insertAdjacentElement(dir, itemEl);
        this.options.dynamic && (this.itemSizes[index].size = getElSpace((itemEl), this.options.axis));
        this.itemSizes[index].rendered = true;
        cb && cb(this.itemSizes[index].size);
    }
    updateSizeOnly(index) {
        let itemEl = this.getItemNode(this.content[index]);
        this.listEl.insertAdjacentElement('beforeend', itemEl);
        this.options.dynamic && (this.itemSizes[index].size = getElSpace((itemEl), this.options.axis));
    }
    renderFromEnd() {
        this.listSize = 0;
        this.listEl.innerHTML = '';
        this.nodes = [];
        this.endIdx = this.content.length - 1;
        for (let i = this.endIdx; i >= 0; i--) {
            this.appendUpdateSize(i, 'afterbegin', (size) => {
                this.listSize += size;
            });
            if (this.listSize > this.targetSize) {
                for (let k = 1; k <= this.options.spill; k++) {
                    this.appendUpdateSize(i - k, 'afterbegin', (size) => {
                        this.listSize += size;
                    });
                }
                this.startIdx = i - this.options.spill;
                break;
            }
        }
        this.get2SetWrapSize();
        this.offsetVal = this.wrapSize - this.listSize;
        this.setContOffset();
        this.setScrollVal(this.wrapSize);
        super.listen({ name: 'rendered', params: [{ startIdx: this.startIdx, endIdx: this.endIdx, nodes: this.nodes, scrollVal: this.scrollVal, offsetVal: this.offsetVal, wrapSize: this.wrapSize }] });
    }
    getPadStartFromIdx(index) {
        if (!this.options.dynamic) {
            return this.avgSize * index;
        }
        else {
            let result = 0;
            for (let i = 0; i < index; i++) {
                result += this.itemSizes[i].size;
            }
            return result;
        }
    }
    setScrollVal(val = this.paddingStart) {
        this.targetEl.removeEventListener('scroll', this.autoRenderEvt);
        this.targetEl[this.propsMap.scroll] = val;
        this.scrollVal = val;
        setTimeout(() => {
            this.targetEl.addEventListener('scroll', this.autoRenderEvt, false);
        }, 0);
    }
    updateScrollVal(start) {
        this.paddingStart = this.getPadStartFromIdx(start);
        this.setScrollVal();
    }
    compensate() {
        this.endIdx = clampVal({ val: this.endIdx + this.options.spill, min: 0, max: this.itemSizes.length - 1 });
        this.startIdx = clampVal({ val: this.startIdx - this.options.spill, min: 0, max: this.itemSizes.length - 1 });
    }
    get2SetContOffset() {
        if (!this.startIdx) {
            this.offsetVal = 0;
        }
        else {
            this.offsetVal = this.paddingStart;
            if (!this.options.dynamic) {
                this.offsetVal -= this.avgSize * (this.options.spill + 1);
            }
            else {
                for (let i = 0; i <= this.options.spill; i++) {
                    this.offsetVal -= this.itemSizes[this.startIdx + i].size;
                }
            }
        }
        this.setContOffset();
    }
    setContOffset(val = this.offsetVal) {
        transformTools.set({
            el: this.contEl,
            data: {
                translate: { [this.options.axis]: Math.max(0, val) }
            }
        });
    }
    scrollTo(index = 0, cb) {
        if (!index) {
            this.targetEl[this.propsMap.scroll] = 0;
        }
        else {
            this.renderFromStart(index);
        }
        cb && cb.call(this, index);
    }
    toStart() {
        this.scrollTo();
    }
    toEnd() {
        this.renderFromEnd();
    }
    async append(data, cb) {
        if (this.destroyed)
            return;
        let resp = await this.getData(data);
        if (resp.content.length) {
            if (this.options.b4Append) {
                let tmp = await this.options.b4Append.call(this, resp.content);
                tmp && (resp.content = tmp);
            }
            this.content.push(...resp.content);
            this.itemSizes.push(...this.getAvgArr(resp.content.length));
            this.renderFromStart();
            super.listen({ name: 'append', cb, params: [resp] });
        }
        return this;
    }
    async updateItem(data = { index: 0, override: true }) {
        if (this.destroyed)
            return;
        let item = this.content[data.index], itemType = getDataType(item), contType = getDataType(data.content);
        if (itemType.includes('HTML')) {
            data.override && (item.innerHTML = '');
            if (contType.includes('HTML')) {
                item.appendChild(data.content);
            }
            else {
                item.insertAdjacentHTML('beforeend', data.content.toString());
            }
        }
        else if (['String', 'Number'].includes(itemType) && ['String', 'Number'].includes(contType)) {
            data.override && (this.content[data.index] = '');
            this.content[data.index] += (data.content + '');
        }
        else if (itemType === 'Object' && contType === 'Object') {
            data.override && (this.content[data.index] = {});
            Object.assign(this.content[data.index], data.content);
        }
        if (this.options.b4UpdateItem) {
            let resp = await this.options.b4UpdateItem.call(this, this.content[data.index]);
            resp && (this.content[data.index] = resp);
        }
        if (itemType.includes('HTML') && !isEmpty(data.attrs)) {
            for (let k in data.attrs) {
                item.setAttribute(k, data.attrs[k]);
            }
        }
        this.updateSizeOnly(data.index);
        this.obsRenderEvt.cancel();
        this.obsRenderEvt();
        super.listen({ name: 'updateItem', cb: data.cb, params: [this.content[data.index]] });
    }
    async clear(cb) {
        if (this.destroyed)
            return;
        this.options.b4Clear && await this.options.b4Clear.call(this);
        this.content = [];
        this.itemSizes = [];
        this.renderFromStart();
        super.listen({ name: 'cleared', cb });
        return this;
    }
    
    destroy(cb) {
        if (this.destroyed)
            return;
        this.targetEl.removeEventListener("scroll", this.autoRenderEvt);
        this.startSpyIns.destroy();
        this.endSpyIns.destroy();
        this.itemObs.disconnect();
        this.targetObs.unobserve(this.targetEl);
        this.contXhr && this.contXhr.abort();
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

const optPagination = [
    {
        attr: 'list-sel',
        prop: 'listSel',
        value: ''
    },
    {
        attr: 'count',
        prop: 'count',
        value: 10
    },
    {
        attr: 'counts',
        prop: 'counts',
        value: ''
    },
    {
        attr: 'dropdown',
        prop: 'dropdown',
        value: {}
    },
    {
        attr: 'current',
        prop: 'current',
        value: 1,
    },
    {
        attr: 'size',
        prop: 'size',
        value: 'md',
    },
    {
        attr: 'expanded',
        prop: 'expanded',
        value: false,
    },
    {
        attr: 'visible',
        prop: 'visible',
        value: {
            edge: 2,
            center: 2,
        },
    },
    {
        attr: 'align',
        prop: 'align',
        value: '',
    },
    {
        attr: 'type',
        prop: 'type',
        value: '',
    },
    {
        attr: 'flexible',
        prop: 'flexible',
        value: false,
    },
    {
        attr: 'layout',
        prop: 'layout',
        value: 'first|prev|pages|next|last',
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'delay',
        prop: 'delay',
        value: 100,
    },
    {
        attr: 'names',
        prop: 'names',
        value: {
            main: 'div',
            total: 'span',
            count: 'span',
            locate: 'span',
            pages: 'span',
            tips: 'span',
            ellipsis: 'span',
            prev: 'a',
            next: 'a',
            first: 'a',
            last: 'a',
            item: 'a',
            section: 'section',
        },
    },
    {
        attr: 'content',
        prop: 'content',
        value: 1000,
        type: 'ignore',
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: '',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'tpl-str',
        prop: 'tplStr',
        value: '',
    },
    {
        attr: 'tpl-eng',
        prop: 'tplEng',
        value: null,
    },
    {
        attr: 'b4-locate',
        prop: 'b4Locate',
        value: null,
    },
    {
        attr: 'b4-renderlist',
        prop: 'b4RenderList',
        value: null,
    },
    {
        attr: 'on-located',
        prop: 'onLocated',
        value: null,
    },
    {
        attr: 'on-gotcont',
        prop: 'onGotCont',
        value: null,
    },
    {
        attr: 'on-tofirst',
        prop: 'onToFirst',
        value: null,
    },
    {
        attr: 'on-tolast',
        prop: 'onToLast',
        value: null,
    },
    {
        attr: 'on-exceeded',
        prop: 'onExceeded',
        value: null,
    },
    {
        attr: 'on-renderedlist',
        prop: 'onRenderedList',
        value: null,
    },
    {
        attr: 'on-renderedpages',
        prop: 'onRenderedPages',
        value: null,
    },
    ...optBase
];

class Pagination extends ModBaseListenCache {
    options = {};
    listEl;
    mainEl;
    pagesEl;
    firstEl;
    lastEl;
    prevEl;
    nextEl;
    totalEl;
    tipsEl;
    locateEl;
    countEl;
    ellEl;
    locateInput;
    locateBtn;
    countInput;
    countBtn;
    items;
    outputObs;
    output;
    content;
    data;
    nodes;
    spyIns;
    dropdwonIns;
    altIns;
    static hostType = 'node';
    static optMaps = optPagination;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            maps: Pagination.optMaps,
            host: elem,
            component: true,
        });
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.setOutputObs();
        this.listEl = getEl(this.options.listSel);
        this.setAttrs();
        this.useTpl();
        this.getNodesTree();
        await this.getInitData();
        this.renderLocateEl();
        this.renderCountEl();
        this.altIns && this.altIns.on('located', (data) => {
            this.locate(data.current);
        });
        super.listen({ name: 'initiated', cb });
        return this;
    }
    setOutputObs() {
        this.outputObs = new Observe({ current: -1, count: this.options.count, total: 0, pages: -1, }, {
            lenient: false,
            onSet: (data) => {
                if (['total', 'count'].includes(data.key)) {
                    if (data.key === 'total') {
                        data.proxy.pages = Math.ceil(data.value / (data.proxy.count || 1)) || 1;
                    }
                    else if (data.key === 'count') {
                        data.proxy.pages = Math.ceil(data.proxy.total / (data.value || 1)) || 1;
                    }
                    data.proxy.current = Math.min(data.proxy.current, data.proxy.pages);
                }
                else if (data.key === 'current') {
                    data.proxy.current = clampVal({ val: data.value, min: 1, max: data.proxy.pages });
                }
                this.renderTotalEl();
                this.renderTipsEl();
            },
            onCompleted: (data) => {
                if (!data.keys.set.includes('pages') && !data.keys.set.includes('current'))
                    return;
                if (isEmpty(this.items) || data.keys.set.includes('pages')) {
                    this.renderPagesEl();
                    this.rerenderListEl();
                }
                else if (data.keys.set.includes('current')) {
                    if (this.hasEll()) {
                        this.renderPagesEl();
                        this.rerenderListEl();
                    }
                    else {
                        this.toggleSelected();
                    }
                }
                if (data.proxy.pages <= 1) {
                    this.setDisabled();
                }
                else {
                    if (data.proxy.current <= 1) {
                        this.setDisabled('start');
                        this.setEnabled('end');
                    }
                    else if (data.proxy.current >= data.proxy.pages) {
                        this.setDisabled('end');
                        this.setEnabled('start');
                    }
                    else {
                        this.setEnabled();
                    }
                }
                this.altIns && this.altIns.locate(data.proxy.current);
            }
        });
        this.output = this.outputObs.proxy;
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}pagination`);
        this.options.align ? this.targetEl.setAttribute('align', this.options.align) : this.targetEl.removeAttribute('align');
        this.options.type ? this.targetEl.setAttribute('type', this.options.type) : this.targetEl.removeAttribute('type');
        this.options.classes && classes(this.targetEl).add(this.options.classes);
        this.targetEl.toggleAttribute('flexible', this.options.flexible);
        this.options.size ? this.targetEl.setAttribute('size', this.options.size) : this.targetEl.removeAttribute('size');
    }
    getNodesTree() {
        this.targetEl.innerHTML = '';
        this.mainEl = createEl(this.options.names.main, { [alias]: 'main' }, this.pagesEl);
        this.pagesEl = createEl(this.options.names.pages, { [alias]: 'pages' });
        this.firstEl = createEl(this.options.names.first, { [alias]: 'first' }, this.options.lang?.first);
        this.lastEl = createEl(this.options.names.last, { [alias]: 'last' }, this.options.lang?.last);
        this.prevEl = createEl(this.options.names.prev, { [alias]: 'prev' }, this.options.lang?.prev);
        this.nextEl = createEl(this.options.names.next, { [alias]: 'next' }, this.options.lang?.next);
        this.totalEl = createEl(this.options.names.total, { [alias]: 'total' });
        this.tipsEl = createEl(this.options.names.tips, { [alias]: 'tips' });
        this.locateEl = createEl(this.options.names.lacate, { [alias]: 'locate' });
        this.countEl = createEl(this.options.names.count, { [alias]: 'count' });
        this.ellEl = createEl(this.options.names.ellipsis, { [alias]: 'ell' }, this.options.lang?.ellipsis);
        super.parseLayout(this.targetEl, this.options.layout, {
            prev: this.prevEl,
            next: this.nextEl,
            first: this.firstEl,
            last: this.lastEl,
            total: this.totalEl,
            count: this.countEl,
            tips: this.tipsEl,
            locate: this.locateEl,
            pages: this.pagesEl,
            group: this.mainEl,
        });
        this.prevEl.onclick = () => {
            this.prev();
        };
        this.nextEl.onclick = () => {
            this.next();
        };
        this.firstEl.onclick = () => {
            this.first();
        };
        this.lastEl.onclick = () => {
            this.last();
        };
    }
    async getInitData() {
        if (typeof this.options.content === 'number' || this.options.contType === 'total') {
            this.respSource = Array(~~this.options.content).fill(null).map((k, i) => { return { index: i }; });
            this.content = this.respSource;
            this.output.total = this.options.content;
        }
        else if (this.options.content.moduleName === 'pagination') {
            this.altIns = this.options.content;
            this.content = [...this.altIns.content];
            this.output.total = this.content.length;
        }
        else if ((typeof this.options.content === 'string') && this.options.contType === 'ins') {
            this.altIns = instance.find(this.options.content, 'pagination');
            this.content = this.altIns ? [...this.altIns.content] : [];
            this.output.total = this.content.length;
        }
        else {
            let resp = await this.getData();
            this.content = resp.content;
        }
        if (this.options.current < 1 || this.options.current > this.output.pages) {
            console.warn('The value is out of range,it has been fixed!');
            super.listen({ name: 'exceeded', params: [{ val: this.options.current, min: 1, max: this.output.pages }] });
        }
        this.updateCur(this.options.current);
        if (this.isSqlReq()) {
            this.data = this.content;
        }
        else {
            this.data = this.sliceData(this.content);
        }
        if (this.output.current === 1) {
            super.listen({ name: 'toFirst' });
        }
        else if (this.output.current === this.output.pages) {
            super.listen({ name: 'toLast' });
        }
        super.listen({ name: 'located', params: [{ current: this.output.current, data: this.data }] });
    }
    async getData(data, cb) {
        let result = { content: [], source: null };
        let contReq = {
            content: this.options.content,
            contType: this.options.contType,
            contData: Object.assign({ current: this.options.current, count: this.options.count }, this.options.contData),
            ajax: {
                spinSel: this.listEl,
                error: (resp) => {
                    super.listen({ name: 'error', params: [resp] });
                },
                xhrName: 'contXhr',
                ...this.options.ajax,
            },
        }, resp = await getContent.call(this, data ? extend({ target: contReq, source: data }) : contReq), respType = getDataType(resp);
        result.source = resp;
        if (respType === 'Array') {
            result.content = resp;
            this.output.total = result.content.length;
        }
        else if (respType === 'Object' && resp.hasOwnProperty('data')) {
            if (Array.isArray(resp.data)) {
                result.content = resp.data;
                this.output.total = result.content.length;
            }
            else {
                result.content = resp?.data?.list || [];
                this.output.total = resp?.data?.total || result.content.length;
            }
        }
        else if (respType === 'String') {
            result.content = tplToEls(resp);
            this.output.total = result.content.length;
        }
        this.respSource = result.source;
        super.listen({ name: 'gotCont', cb, params: [result] });
        return result;
    }
    isSqlReq(resp = this.respSource) {
        return resp?.data?.hasOwnProperty('list') && resp?.data?.hasOwnProperty('total');
    }
    sliceData(data = this.content, current = this.output.current) {
        let start = (current - 1) * this.output.count;
        return data.slice(start, start + this.output.count);
    }
    async getCurCont(current = this.output.current) {
        current = this.correctCur(current);
        let result;
        if (this.isSqlReq()) {
            let resp = await this.getData({ contData: { current, count: this.output.count } });
            this.content = resp.content;
            result = resp.content;
        }
        else {
            result = this.sliceData(this.content, current);
        }
        return result;
    }
    getItemNode(data) {
        let dataType = getDataType(data), result;
        if (dataType.includes('HTML')) {
            result = data;
        }
        else {
            result = createEl(this.options.names.section, {});
            result.innerHTML = dataType === 'Object' ? super.getTplcont(data) : data.toString();
        }
        return result;
    }
    renderTotalEl() {
        this.totalEl.innerHTML = renderTpl(this.options.lang?.total, this.output);
    }
    renderTipsEl() {
        this.tipsEl.innerHTML = renderTpl(this.options.lang?.tips, this.output);
    }
    renderLocateEl() {
        let tmp = { size: 'sm', feature: 'plain', label: this.options.lang?.locate, unit: this.options.lang?.page, value: this.output.current, btn: `<i class='_icon-arrow-right'></i>` };
        this.locateInput = createEl('ax-input', tmp);
        this.locateInput.on('connected', () => {
            this.locateInput.btnEl.onclick = debounce(() => {
                let val = ~~this.locateInput.value;
                (val < 1 || val > this.output.pages) && console.warn('The value is out of range,it has been fixed!');
                val = this.correctCur(val);
                this.locateInput.value = val;
                this.locate(val);
            }, this.options.delay);
            this.locateInput.onkeyup = (e) => (e.code === 'Enter') && this.locateInput.btnEl.click();
        });
        this.locateEl.appendChild(this.locateInput);
    }
    renderCountEl() {
        let tmp = { size: 'sm', feature: 'plain', label: this.options.lang?.count, unit: this.options.lang?.unit, value: this.output.count, btn: `<i class='_icon-arrow-right'></i>` };
        this.countInput = createEl('ax-input', tmp);
        this.countInput.on('connected', () => {
            this.countInput.btnEl.onclick = debounce(() => {
                this.output.count = ~~this.countInput.value;
            }, this.options.delay);
            this.countInput.onkeyup = (e) => (e.code === 'Enter') && this.countInput.btnEl.click();
            if (this.options.counts) {
                this.dropdwonIns = new ax.Dropdown(this.countInput, {
                    content: this.options.counts,
                    feature: 'select',
                    popup: {
                        canClick: (target) => {
                            return this.countInput.inputEl.contains(target);
                        },
                        onTargetSet: (val) => {
                            this.countInput.btnEl.click();
                        }
                    }
                });
            }
        });
        this.countEl.appendChild(this.countInput);
    }
    renderPagesEl(current = this.output.current) {
        this.pagesEl.innerHTML = '';
        this.items = [];
        for (let i = 1; i <= this.output.pages; i++) {
            if (this.options.expanded) {
                this.items.push(this.createItem(i));
            }
            else {
                let condL = i > this.options.visible?.edge && i < current - this.options.visible?.center, condR = i > current + this.options.visible?.center && i <= this.output.pages - this.options.visible?.edge;
                if (condL || condR) {
                    if (this.items.at(-1) && this.items.at(-1).getAttribute(alias) === 'ell') {
                        continue;
                    }
                    else {
                        let tmp = this.ellEl.cloneNode(false);
                        tmp.innerHTML = renderTpl(this.options.lang?.ellipsis, this.output);
                        this.items.push(tmp);
                    }
                }
                else {
                    this.items.push(this.createItem(i));
                }
            }
        }
        this.pagesEl.append(...this.items);
        super.listen({ name: 'renderedPages', params: [{ nodes: this.items, ...this.output }] });
    }
    createItem(current) {
        let tmp = createEl(this.options.names.item, { [alias]: 'page', page: current }, current);
        this.output.current == current && tmp.setAttribute('selected', '');
        tmp.onclick = debounce(() => {
            this.locate(current);
        }, this.options.delay);
        return tmp;
    }
    hasEll() {
        return this.items.find((k) => k.getAttribute(alias, 'ell'));
    }
    correctCur(current) {
        return clampVal({ val: current, min: 1, max: this.output.pages });
    }
    updateCur(current = this.output.current) {
        this.output.current = ~~current;
    }
    async rerenderListEl() {
        if (this.destroyed || !this.listEl)
            return;
        if (this.options.b4RenderList) {
            let resp = await this.options.b4RenderList.call(this, this.data);
            resp && (this.data = resp);
        }
        this.nodes = [];
        for (let k of this.data) {
            let tmp = this.getItemNode(k);
            tmp && this.nodes.push(tmp);
        }
        this.listEl.innerHTML = '';
        this.listEl.append(...this.nodes);
        super.listen({ name: 'renderedList', params: [{ nodes: this.nodes, data: this.data, wrap: this.listEl, ...this.output }] });
    }
    toggleSelected(current = this.output.current) {
        let item = this.items.find((k) => k.getAttribute('page') == current);
        if (item) {
            item.setAttribute('selected', '');
            let tmp = this.items.find((k) => k !== item && k.hasAttribute('selected'));
            tmp && tmp.removeAttribute('selected');
        }
    }
    render({ content = [], tplStr = this.tplStr, tplEng = this.tplEng, nodeName = this.options.names.section, wrapSel }) {
        if (this.destroyed)
            return;
        if (isEmpty(content))
            return;
        this.nodes = [];
        let wrapEl = getEl(wrapSel);
        for (let k of content) {
            let str = super.getTplcont(k, tplStr, tplEng), node = createEl(nodeName, {}, str);
            this.nodes.push(node);
        }
        if (wrapEl) {
            wrapEl.innerHTML = '';
            wrapEl.append(...this.nodes);
        }
        super.listen({ name: 'renderedList', params: [{ nodes: this.nodes, data: content, wrap: wrapEl, ...this.output }] });
    }
    async locate(current, cb) {
        if (this.destroyed)
            return;
        current = ~~current;
        if (this.options.b4Locate) {
            let resp = await this.options.b4Locate.call(this, { to: current, ...this.output });
            if (!isNull(resp)) {
                typeof resp === 'number' ? (current = resp) : (current = ~~resp);
            }
        }
        if (current < 1 || current > this.output.pages) {
            console.warn('The value is out of range,it has been fixed!');
            super.listen({ name: 'exceeded', params: [{ val: current, min: 1, max: this.output.pages }] });
        }
        current = this.correctCur(current);
        this.data = await this.getCurCont(current);
        this.updateCur(current);
        if (this.output.current === 1) {
            super.listen({ name: 'toFirst' });
        }
        else if (this.output.current === this.output.pages) {
            super.listen({ name: 'toLast' });
        }
        super.listen({ name: 'located', cb, params: [{ current, data: this.data }] });
    }
    prev() {
        this.locate(this.output.current - 1);
    }
    next() {
        this.locate(this.output.current + 1);
    }
    first() {
        this.locate(1);
    }
    last() {
        this.locate(this.output.pages);
    }
    setDisabled(type = 'both') {
        if (type === 'start') {
            this.prevEl.setAttribute('disabled', '');
            this.firstEl.setAttribute('disabled', '');
        }
        else if (type === 'end') {
            this.nextEl.setAttribute('disabled', '');
            this.lastEl.setAttribute('disabled', '');
        }
        else {
            this.prevEl.setAttribute('disabled', '');
            this.firstEl.setAttribute('disabled', '');
            this.nextEl.setAttribute('disabled', '');
            this.lastEl.setAttribute('disabled', '');
        }
    }
    setEnabled(type = 'both') {
        if (type === 'start') {
            this.prevEl.removeAttribute('disabled');
            this.firstEl.removeAttribute('disabled');
        }
        else if (type === 'end') {
            this.nextEl.removeAttribute('disabled');
            this.lastEl.removeAttribute('disabled');
        }
        else {
            this.prevEl.removeAttribute('disabled');
            this.firstEl.removeAttribute('disabled');
            this.nextEl.removeAttribute('disabled');
            this.lastEl.removeAttribute('disabled');
        }
    }
    
    destroy(cb) {
        if (this.destroyed)
            return this;
        this.spyIns.destroy();
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

let AXTMP_separator$2 = config.rangeHyphen, AXTMP_hyphen = config.rangeHyphen;
const optRange = [
    {
        attr: 'name',
        prop: 'name',
        value: ''
    },
    {
        attr: 'step',
        prop: 'step',
        value: 1,
    },
    {
        attr: 'max',
        prop: 'max',
        value: 100,
    },
    {
        attr: 'min',
        prop: 'min',
        value: 0,
    },
    {
        attr: 'value',
        prop: 'value',
        value: 0,
    },
    {
        attr: 'multiple',
        prop: 'multiple',
        value: false,
    },
    {
        attr: 'full',
        prop: 'full',
        value: false,
    },
    {
        attr: 'locked',
        prop: 'locked',
        value: false,
    },
    {
        attr: 'limit-show',
        prop: 'limitShow',
        value: true,
    },
    {
        attr: 'tip-show',
        prop: 'tipShow',
        value: true,
    },
    {
        attr: 'size',
        prop: 'size',
        value: 'md',
    },
    {
        attr: 'fence',
        prop: 'fence',
        value: {
            enable: false,
            min: 0,
            max: 100,
        },
    },
    {
        attr: 'button',
        prop: 'button',
        value: {
            enable: false,
            decrease: `<i class="${prefix}icon-minus-o-f"></i>`,
            increase: `<i class="${prefix}icon-plus-o-f"></i>`,
        }
    },
    {
        attr: 'ruler',
        prop: 'ruler',
        value: {
            enable: false,
            majorEqual: 4,
            minorEqual: 'auto',
            labels: [],
        }
    },
    {
        attr: 'result',
        prop: 'result',
        value: {
            enable: false,
            target: null,
        }
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'keyboard',
        prop: 'keyboard',
        value: false,
    },
    {
        attr: 'separator',
        prop: 'separator',
        value: AXTMP_separator$2,
    },
    {
        attr: 'hyphen',
        prop: 'hyphen',
        value: AXTMP_hyphen,
    },
    {
        attr: 'axis',
        prop: 'axis',
        value: 'x',
    },
    {
        attr: 'disabled',
        prop: 'disabled',
        value: false,
    },
    {
        attr: 'on-set',
        prop: 'onSet',
        value: null,
    },
    {
        attr: 'on-tostart',
        prop: 'onToStart',
        value: null,
    },
    {
        attr: 'on-toend',
        prop: 'onToEnd',
        value: null,
    },
    {
        attr: 'on-enabled',
        prop: 'onEnabled',
        value: null,
    },
    {
        attr: 'on-disabled',
        prop: 'onDisabled',
        value: null,
    },
    {
        attr: 'on-restored',
        prop: 'onRestored',
        value: null,
    },
    ...optBase
];

class Range extends ModBaseListenCache {
    options = {};
    inputEl;
    axisCoef;
    resizeObs;
    sizes;
    keyEvt;
    focusEvt;
    focusHandle;
    handles;
    gestures;
    fence;
    resultParent;
    initVal;
    rawVal;
    places;
    output;
    decrEl;
    incrEl;
    resultEl;
    wrapEl;
    mainEl;
    trackEl;
    thumbEl;
    baseEl;
    bubbleEl;
    bubbles;
    handleEl;
    rulerEl;
    tickEl;
    tmpRatios;
    offset;
    ceilValue;
    ticks;
    fenceEl;
    propsMap;
    static hostType = 'node';
    static optMaps = optRange;
    constructor(elem, options = {}, initial = true) {
        super();
        let tmp = getEl(elem), host;
        if (!tmp)
            return this;
        if (tmp.nodeName === 'INPUT') {
            this.inputEl = tmp;
            this.inputEl.type = 'hidden';
            host = createEl('div', { class: `${prefix}range` });
            tmp.insertAdjacentElement('beforebegin', host);
            host.appendChild(tmp);
        }
        else {
            host = tmp;
            host.classList.add(`${prefix}range`);
            this.inputEl = createEl('input', { type: 'hidden', value: tmp.textContent.trim() });
            tmp.innerHTML = '';
            host.appendChild(this.inputEl);
        }
        super.ready({
            options,
            maps: Range.optMaps,
            host,
            component: true,
            spread: ['button', 'ruler', 'result', 'fence']
        });
        if (this.options.axis === 'x') {
            this.propsMap = propsMap[this.options.axis];
        }
        else {
            this.propsMap = propsMap[this.options.axis];
            this.propsMap.position = 'bottom';
        }
        this.axisCoef = this.options.axis === 'y' ? -1 : 1;
        this.resizeObs = new ResizeObserver(debounce(() => {
            this.updateSizes();
        }));
        this.sizes = {
            handle: 0,
            track: 0,
            ruler: 0,
            thumb: 0,
            bubble: { min: 0, max: 0, from: 0, to: 0, single: 0 },
            ratio: { radius: 0, min: 0, max: 0, full: 0 },
            offset: {
                from: { min: 0, max: 0 },
                to: { min: 0, max: 0 },
                single: { min: 0, max: 0 },
            }
        };
        let _this = this;
        this.keyEvt = function (e) {
            if (!['ArrowLeft', 'ArrowDown', 'ArrowRight', 'ArrowUp'].includes(e.code))
                return;
            preventDft(e, true);
            _this.focusHandle = this;
            if (['ArrowLeft', 'ArrowDown'].includes(e.code)) {
                _this.decrease();
            }
            else {
                _this.increase();
            }
        };
        this.focusEvt = function () {
            this.focus();
        };
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.handles = { from: null, to: null, single: null };
        this.gestures = { from: null, to: null, single: null, thumb: null };
        this.correctOpts();
        this.resultParent = getEl(this.options.result.target);
        this.getInitVal();
        this.rawVal = Array.isArray(this.options.value) ? [...this.initVal] : this.initVal;
        this.getPlaces();
        this.output = { multiple: this.options.multiple, raw: '', value: '', range: [0, 0], ratio: [0, 0] };
        this.initRender();
        this.setAttrs();
        this.resizeObs.observe(this.trackEl);
        this.updateSizes();
        this.setVals(this.initVal);
        this.ticks = [];
        this.setTicks();
        this.setFence();
        if (!this.options.disabled) {
            if (this.options.button.enable) {
                this.decrEl.onclick = () => this.decrease();
                this.incrEl.onclick = () => this.increase();
            }
            if (this.options.multiple) {
                this.setHandleIns('from');
                this.setHandleIns('to');
                this.tmpRatios = [...this.output.ratio];
                this.gestures.thumb = new Gesture(this.thumbEl, {
                    onTranslating: (e) => {
                        this.slideSet(this.thumbEl, e.translate.diff[this.options.axis] * this.axisCoef);
                    },
                    onTranslated: () => {
                        this.tmpRatios = [...this.output.ratio];
                    }
                });
            }
            else {
                this.setHandleIns('single');
            }
            this.baseEl.onclick = (e) => {
                let clientObj = getClientObj(e), offset;
                if (!clientObj) {
                    return false;
                }
                offset = (clientObj[this.options.axis] - this.baseEl.getBoundingClientRect()[this.propsMap.position]) * this.axisCoef;
                if (this.options.multiple) {
                    let fromOffset = this.output.ratio[0] * this.sizes.track, toOffset = this.output.ratio[1] * this.sizes.track;
                    if (offset > toOffset) {
                        this.slideSet(this.thumbEl, offset - toOffset - this.sizes.handle / 2);
                    }
                    else if (offset < toOffset) {
                        this.slideSet(this.thumbEl, offset - fromOffset - this.sizes.handle / 2);
                    }
                    this.tmpRatios = [...this.output.ratio];
                }
                else {
                    this.slideSet(this.handles.single, offset - this.sizes.handle / 2);
                }
            };
            if (!this.options.multiple) {
                this.thumbEl.onclick = (e) => {
                    let clientObj = getClientObj(e), offset;
                    if (clientObj) {
                        offset = (clientObj[this.options.axis] - this.baseEl.getBoundingClientRect()[this.propsMap.position]) * this.axisCoef;
                        this.slideSet(this.handles.single, offset - this.sizes.handle / 2);
                    }
                };
            }
            if (this.bubbles.min) {
                this.bubbles.min.onclick = () => {
                    this.toStart();
                };
            }
            if (this.bubbles.max) {
                this.bubbles.max.onclick = () => {
                    this.toEnd();
                };
            }
            if (this.options.keyboard) {
                for (let k in this.handles) {
                    let tmp = this.handles[k];
                    if (tmp) {
                        tmp.removeEventListener('keydown', this.keyEvt);
                        tmp.addEventListener('keydown', this.keyEvt, false);
                        tmp.removeEventListener('pointerup', this.focusEvt);
                        tmp.addEventListener('pointerup', this.focusEvt, false);
                    }
                }
            }
        }
        super.listen({ name: 'initiated', cb });
        return this;
    }
    correctOpts() {
        this.options.max = parseFloat(this.options.max);
        this.options.min = parseFloat(this.options.min);
        this.options.step = parseFloat(this.options.step);
        if (this.options.fence.enable) {
            this.fence = {
                min: clampVal({ val: this.options.fence.min, min: this.options.min, max: this.options.max }),
                max: clampVal({ val: this.options.fence.max, min: this.options.min, max: this.options.max })
            };
        }
        else {
            this.fence = { min: this.options.min, max: this.options.max };
        }
    }
    updateSizes() {
        let handle = (this.handles.single || this.handles.from).getBoundingClientRect()[this.propsMap.size], track = this.trackEl.getBoundingClientRect()[this.propsMap.size], min = this.bubbles.min ? this.bubbles.min.getBoundingClientRect()[this.propsMap.size] : 0, max = this.bubbles.max ? this.bubbles.max.getBoundingClientRect()[this.propsMap.size] : 0, from = this.bubbles.from ? this.bubbles.from.getBoundingClientRect()[this.propsMap.size] : 0, to = this.bubbles.to ? this.bubbles.to.getBoundingClientRect()[this.propsMap.size] : 0, single = this.bubbles.single ? this.bubbles.single.getBoundingClientRect()[this.propsMap.size] : 0, fullRatio = (track - handle) / track, radiusRadio = (handle / 2) / track, minRatio = ((this.fence.min - this.options.min) / (this.options.max - this.options.min)) * fullRatio, maxRatio = ((this.fence.max - this.options.min) / (this.options.max - this.options.min)) * fullRatio;
        extend({
            target: this.sizes,
            source: {
                handle, track,
                bubble: { min, max, from, to, single },
                ratio: { radius: radiusRadio, min: minRatio, max: maxRatio, full: fullRatio },
            }
        });
    }
    setHandleIns(type) {
        this.gestures[type] = new Gesture(this.handles[type], {
            onTranslate: () => {
                this.offset = style(this.handles[type])[this.propsMap.position];
            },
            onTranslating: (e) => {
                this.slideSet(this.handles[type], parseFloat(this.offset) + e.translate.diff[this.options.axis] * this.axisCoef);
            },
            onTranslated: () => {
                type !== 'single' && (this.tmpRatios = [...this.output.ratio]);
            }
        });
    }
    setHandleActive(el) {
        el.setAttribute('active', '');
        for (let k in this.handles) {
            this.handles[k] !== el && (this.handles[k].removeAttribute('active'));
        }
    }
    getRatioFromVal(value) {
        return ((value - this.options.min) / (this.options.max - this.options.min)) * this.sizes.ratio.full;
    }
    getValFromRatio(ratio) {
        return ratio * (this.options.max - this.options.min) / this.sizes.ratio.full + this.options.min;
    }
    decrease(step = this.options.step) {
        let isToFocus = this.handles.to && this.focusHandle === this.handles.to, min = isToFocus ? this.output.range[0] : this.fence.min, value = (this.output.range[isToFocus ? 1 : 0] - step), handleDiff = (this.output.range[1] - this.output.range[0]);
        if (this.options.multiple) {
            value < min && (value = min);
            if (this.options.locked) {
                this.setVals([value, value + handleDiff]);
            }
            else {
                this.setVals(isToFocus ? [this.output.range[0], value] : [value, this.output.range[1]]);
            }
        }
        else {
            this.setVals(this.output.range[0] - step);
        }
        this.options.multiple && (this.tmpRatios = [...this.output.ratio]);
    }
    increase(step = this.options.step) {
        let isFromFocus = this.handles.from && this.focusHandle === this.handles.from, max = isFromFocus ? this.output.range[1] : this.fence.max, value = (this.output.range[isFromFocus ? 0 : 1] + step), handleDiff = (this.output.range[1] - this.output.range[0]);
        if (this.options.multiple) {
            value > max && (value = max);
            if (this.options.locked) {
                this.setVals([value - handleDiff, value]);
            }
            else {
                this.setVals(isFromFocus ? [value, this.output.range[1]] : [this.output.range[0], value]);
            }
        }
        else {
            this.setVals(this.output.range[0] + step);
        }
        this.options.multiple && (this.tmpRatios = [...this.output.ratio]);
    }
    setAttrs() {
        this.targetEl.setAttribute('axis', this.options.axis);
        classes(this.targetEl).add(this.options.classes);
        this.targetEl.toggleAttribute('multiple', this.options.multiple);
        this.targetEl.toggleAttribute('full', this.options.full);
        this.targetEl.toggleAttribute('inert', this.options.disabled);
        this.targetEl.setAttribute('size', this.options.size);
    }
    getInitVal() {
        if (this.inputEl.value) {
            if (this.inputEl.value.includes(this.options.separator)) {
                this.options.multiple = true;
                this.initVal = this.inputEl.value.split(this.options.separator).map((k) => parseFloat(k)).sort((a, b) => a - b);
            }
            else {
                this.initVal = this.options.multiple ? [parseFloat(this.inputEl.value), this.options.max] : parseFloat(this.inputEl.value);
            }
        }
        else {
            if (this.options.multiple && !Array.isArray(this.options.value)) {
                let tmp = typeof this.options.value === 'number' ? [this.options.value] :
                    (this.options.value.split(this.options.separator).slice(0, 2).map((k) => parseFloat(k)).sort((a, b) => a - b));
                this.initVal = [isNull(tmp[0]) ? this.options.min : tmp[0], isNull(tmp[1]) ? this.options.max : tmp[1]];
            }
            else if (!this.options.multiple && Array.isArray(this.options.value)) {
                this.initVal = this.options.value[0];
            }
            else if (typeof this.options.value === 'string' && this.options.value.includes(this.options.separator)) {
                this.options.multiple = true;
                this.initVal = this.options.value.split(this.options.separator).map((k) => parseFloat(k)).sort((a, b) => a - b);
            }
            else {
                this.initVal = Array.isArray(this.options.value) ? [...this.options.value].sort((a, b) => a - b) : this.options.value;
            }
        }
    }
    getPlaces() {
        let stepPlaces = this.options.step.toString().split('.')[1] ? this.options.step.toString().split('.')[1].length : 0, endsPlaces = Math.max(getPlaces(this.options.min), getPlaces(this.options.max));
        this.places = stepPlaces > 0 ? stepPlaces : endsPlaces;
    }
    correctVal(value) {
        let obj = { min: this.fence.min, max: this.fence.max };
        if (Array.isArray(value)) {
            let tmp = value.map((k) => toNumber(clampVal({ val: k, ...obj }), { places: this.places }));
            this.options.multiple && tmp.sort((a, b) => a - b);
            return tmp;
        }
        else if (value === '' || isNull(value)) {
            return [this.fence.min, this.fence.max];
        }
        else {
            let tmp;
            if (typeof value === 'string' && value.includes(this.options.separator)) {
                tmp = value.split(this.options.separator).map((k) => parseFloat(k)).sort((a, b) => a - b);
            }
            else {
                tmp = [toNumber(clampVal({ val: value, ...obj }), { places: this.places }), this.output.range[1]];
            }
            return this.options.multiple ? tmp.sort((a, b) => a - b) : tmp;
        }
    }
    initRender() {
        if (this.wrapEl) {
            this.wrapEl.innerHTML = '';
            this.options.name && (this.inputEl.name = this.options.name);
            this.inputEl.value = '';
        }
        else {
            this.wrapEl = createEl('div', { class: `${prefix}range-wrap` });
            this.targetEl.appendChild(this.wrapEl);
            !this.inputEl.name && (this.inputEl.name = this.options.name);
        }
        this.bubbles = {};
        this.wrapEl.innerHTML = `
        <div class="${prefix}range-main">
            <div class="${prefix}range-track">
                <div ${alias}="base"></div>
                <div ${alias}="thumb"></div>
            </div>
            <div class="${prefix}range-bubble">
                ${this.options.limitShow ? '<span ' + alias + '="min">' + this.options.min + '</span><span ' + alias + '="max">' + this.options.max + '</span>' : ''}
            </div>
            <div class="${prefix}range-handle"></div>
            <div class="${prefix}range-ruler"><ul></ul></div>
        </div>
        `;
        this.mainEl = this.wrapEl.querySelector(`.${prefix}range-main`);
        this.trackEl = this.wrapEl.querySelector(`.${prefix}range-track`);
        this.thumbEl = this.wrapEl.querySelector(`[${alias}="thumb"]`);
        this.baseEl = this.wrapEl.querySelector(`[${alias}="base"]`);
        this.bubbleEl = this.wrapEl.querySelector(`.${prefix}range-bubble`);
        this.bubbles.min = this.bubbleEl.querySelector(`[${alias}="min"]`);
        this.bubbles.max = this.bubbleEl.querySelector(`[${alias}="max"]`);
        this.handleEl = this.wrapEl.querySelector(`.${prefix}range-handle`);
        this.rulerEl = this.wrapEl.querySelector(`.${prefix}range-ruler`);
        this.tickEl = this.rulerEl.querySelector('ul');
        this.decrEl = createEl('span', { class: `${prefix}range-btn`, [alias]: 'decrease' }, this.options.button.decrease);
        this.incrEl = createEl('span', { class: `${prefix}range-btn`, [alias]: 'increase' }, this.options.button.increase);
        if (this.options.button.enable) {
            this.mainEl.insertAdjacentElement('beforebegin', this.decrEl);
            this.mainEl.insertAdjacentElement('afterend', this.incrEl);
        }
        this.resultEl = createEl('span', { class: `${prefix}range-result` });
        if (this.options.result.enable) {
            this.resultParent ? this.resultParent.appendChild(this.resultEl) : this.wrapEl.appendChild(this.resultEl);
        }
        if (this.options.multiple) {
            this.handles.from = createEl('span', { [alias]: 'from', tabindex: 0 });
            this.handles.to = createEl('span', { [alias]: 'to', tabindex: 0 });
            this.handleEl.append(this.handles.from, this.handles.to);
            if (this.options.tipShow) {
                this.bubbles.from = createEl('span', { [alias]: 'from' });
                this.bubbles.to = createEl('span', { [alias]: 'to' });
                this.bubbles.merge = createEl('span', { [alias]: 'merge' });
                this.bubbleEl.append(this.bubbles.from, this.bubbles.to, this.bubbles.merge);
            }
        }
        else {
            this.handles.single = createEl('span', { [alias]: 'single', tabindex: 0 });
            this.handleEl.append(this.handles.single);
            if (this.options.tipShow) {
                this.bubbles.single = createEl('span', { [alias]: 'single' });
                this.bubbleEl.append(this.bubbles.single);
            }
        }
    }
    setVals(value, cb) {
        if (this.destroyed)
            return;
        this.output.range = this.correctVal(value);
        let radiusPercent = this.sizes.ratio.radius * 100, getTipHTML = (val) => toNumber(val, { places: this.places });
        if (this.options.multiple) {
            this.output.ratio = [this.getRatioFromVal(this.output.range[0]), this.getRatioFromVal(this.output.range[1])];
            let fromPercent = this.output.ratio[0] * 100, toPercent = this.output.ratio[1] * 100;
            this.handles.from.style[this.propsMap.position] = `${fromPercent}%`;
            this.handles.to.style[this.propsMap.position] = `${toPercent}%`;
            this.sizes.ratio.center = this.output.ratio[1];
            this.thumbEl.style[this.propsMap.size] = `${toPercent - fromPercent}%`;
            this.thumbEl.style[this.propsMap.position] = `${fromPercent + radiusPercent}%`;
            this.sizes.thumb = this.thumbEl.getBoundingClientRect()[this.propsMap.size];
            if (this.options.tipShow) {
                this.bubbles.from.style[this.propsMap.position] = `${fromPercent}%`;
                this.bubbles.from.innerHTML = getTipHTML(this.output.range[0]);
                this.bubbles.to.style[this.propsMap.position] = `${toPercent}%`;
                this.bubbles.to.innerHTML = getTipHTML(this.output.range[1]);
                this.bubbles.merge.style[this.propsMap.position] = `${fromPercent + (toPercent - fromPercent) / 2}%`;
                this.bubbles.merge.innerHTML = `${getTipHTML(this.output.range[0])}<i>${this.options.hyphen}</i>${getTipHTML(this.output.range[1])}`;
                this.sizes.bubble.from = this.bubbles.from.getBoundingClientRect()[this.propsMap.size];
                this.sizes.bubble.to = this.bubbles.to.getBoundingClientRect()[this.propsMap.size];
                this.mergeTips();
            }
        }
        else {
            this.output.ratio[0] = this.getRatioFromVal(this.output.range[0]);
            let singlePercent = this.output.ratio[0] * 100;
            this.handles.single.style[this.propsMap.position] = `${singlePercent}%`;
            this.thumbEl.style[this.propsMap.size] = `${singlePercent + radiusPercent}%`;
            this.sizes.thumb = this.thumbEl.getBoundingClientRect()[this.propsMap.size];
            if (this.options.tipShow) {
                this.bubbles.single.style[this.propsMap.position] = `${singlePercent}%`;
                this.bubbles.single.innerHTML = getTipHTML(this.output.range[0]);
                this.sizes.bubble.single = this.bubbles.single.getBoundingClientRect()[this.propsMap.size];
            }
        }
        this.output.value = (this.options.multiple ? this.output.range.join(this.options.separator) : this.output.range[0]).toString();
        this.output.raw = this.inputEl.value;
        this.options.limitShow && this.hideEnds();
        this.inputEl.value = this.output.value;
        this.options.result.enable && (this.resultEl.innerHTML = renderTpl(this.options.lang.result, this.output));
        super.listen({ name: 'output', params: [this.output] });
        super.listen({ name: 'set', cb, params: [this.output] });
        this.endsListen(cb);
        return this;
    }
    endsListen(cb) {
        if (this.options.multiple) {
            if (this.output.range[0] <= this.fence.min) {
                this.decrEl.toggleAttribute('inert', true);
                super.listen({ name: 'toStart', cb, params: [this.fence] });
            }
            else {
                this.decrEl.removeAttribute('inert');
            }
            if (this.output.range[1] >= this.fence.max) {
                this.incrEl.toggleAttribute('inert', true);
                super.listen({ name: 'toEnd', cb, params: [this.fence] });
            }
            else {
                this.incrEl.removeAttribute('inert');
            }
        }
        else {
            if (this.output.range[0] <= this.fence.min) {
                this.decrEl.toggleAttribute('inert', true);
                super.listen({ name: 'toStart', cb, params: [this.fence] });
            }
            else if (this.output.range[0] >= this.fence.max) {
                this.incrEl.toggleAttribute('inert', true);
                super.listen({ name: 'toEnd', cb, params: [this.fence] });
            }
            else {
                this.decrEl.removeAttribute('inert');
                this.incrEl.removeAttribute('inert');
            }
        }
    }
    getVals() {
        return this.output;
    }
    toEnd(cb) {
        if (this.destroyed)
            return;
        let value, diff;
        if (this.options.multiple) {
            diff = this.fence.max - this.output.range[1];
            value = [this.output.range[0] + diff, this.output.range[1] + diff];
        }
        else {
            value = this.fence.max;
        }
        this.setVals(value);
        cb && cb.call(this, value);
        return this;
    }
    toStart(cb) {
        if (this.destroyed)
            return;
        let value, diff;
        if (this.options.multiple) {
            diff = this.fence.min - this.output.range[0];
            value = [this.output.range[0] + diff, this.output.range[1] + diff];
        }
        else {
            value = this.fence.min;
        }
        this.setVals(value);
        cb && cb.call(this, value);
        return this;
    }
    slideSet(dom, value) {
        if (this.destroyed)
            return;
        let result;
        if (dom === this.thumbEl && this.options.multiple) {
            let fromRatio = this.tmpRatios[0] + value / this.sizes.track, diffRatio = this.tmpRatios[1] - this.tmpRatios[0], toRatio = fromRatio + diffRatio, from = this.getValFromRatio(fromRatio), to = this.getValFromRatio(toRatio), diff = this.getValFromRatio(toRatio) - this.getValFromRatio(fromRatio);
            if (this.options.locked) {
                if (from < this.fence.min) {
                    result = [this.fence.min, this.fence.min + diff];
                }
                else if (to > this.fence.max) {
                    result = [this.fence.max - diff, this.fence.max];
                }
                else {
                    result = [from, to];
                }
            }
            else {
                result = [from, to];
            }
        }
        else {
            let ratio = (value / this.sizes.track);
            if (dom === this.handles.from) {
                if (ratio > this.output.ratio[1]) {
                    ratio = this.output.ratio[1];
                }
                else if (ratio < this.sizes.ratio.min) {
                    ratio = this.sizes.ratio.min;
                }
                result = [this.getValFromRatio(ratio), this.output.range[1]];
            }
            else if (dom === this.handles.to) {
                if (ratio > this.sizes.ratio.max) {
                    ratio = this.sizes.ratio.max;
                }
                else if (ratio < this.output.ratio[0]) {
                    ratio = this.output.ratio[0];
                }
                result = [this.output.range[0], this.getValFromRatio(ratio)];
            }
            else if (dom === this.handles.single) {
                if (ratio > this.sizes.ratio.max) {
                    ratio = this.sizes.ratio.max;
                }
                else if (ratio < this.sizes.ratio.min) {
                    ratio = this.sizes.ratio.min;
                }
                result = this.getValFromRatio(ratio);
            }
        }
        this.setVals(result);
    }
    mergeTips() {
        if (this.sizes.thumb < (this.sizes.bubble.from + this.sizes.bubble.to) / 2) {
            this.targetEl.setAttribute('merged', '');
        }
        else {
            this.targetEl.removeAttribute('merged');
        }
    }
    hideEnds() {
        if (this.options.multiple) {
            let offsetFrom = this.output.ratio[0] * this.sizes.track, offsetTo = this.output.ratio[1] * this.sizes.track;
            if (offsetFrom < this.sizes.bubble.min + this.sizes.bubble.from / 2 - this.sizes.handle / 2) {
                this.targetEl.setAttribute('hidemin', '');
            }
            else {
                this.targetEl.removeAttribute('hidemin');
            }
            if (this.sizes.track - offsetTo < this.sizes.bubble.max + this.sizes.bubble.from / 2 + this.sizes.handle / 2) {
                this.targetEl.setAttribute('hidemax', '');
            }
            else {
                this.targetEl.removeAttribute('hidemax');
            }
        }
        else {
            let offsetSingle = this.output.ratio[0] * this.sizes.track;
            if (offsetSingle < this.sizes.bubble.min + this.sizes.bubble.single / 2 - this.sizes.handle / 2) {
                this.targetEl.setAttribute('hidemin', '');
            }
            else {
                this.targetEl.removeAttribute('hidemin');
            }
            if (this.sizes.track - offsetSingle < this.sizes.bubble.max + this.sizes.bubble.single / 2 + this.sizes.handle / 2) {
                this.targetEl.setAttribute('hidemax', '');
            }
            else {
                this.targetEl.removeAttribute('hidemax');
            }
        }
    }
    setTicks() {
        if (!this.options.ruler.enable)
            return;
        this.ceilValue = (this.options.max - this.options.min) / this.options.ruler.majorEqual;
        this.sizes.ruler = this.rulerEl.getBoundingClientRect()[this.propsMap.size];
        for (let k = 0; k <= this.options.ruler.majorEqual; k++) {
            let value = toNumber((this.options.min + this.ceilValue * k), { places: this.places }), content = this.options.ruler.labels.length > 0 ? this.options.ruler.labels[k] : value, offsetRatio = k / this.options.ruler.majorEqual, majorEl = createEl('li', { style: `${this.propsMap.position}:${offsetRatio * 100}%`, 'data-content': content });
            this.tickEl.appendChild(majorEl);
            majorEl.onclick = () => {
                this.setVals(value);
            };
            this.ticks.push({
                index: k,
                dom: majorEl,
                ratio: offsetRatio,
                content: content,
                value,
            });
            if (k !== this.options.ruler.majorEqual) {
                this.setMinorTicks(offsetRatio);
            }
        }
    }
    setFence() {
        if (!this.options.fence.enable)
            return;
        let fullSize = (this.options.max - this.options.min), sizeRatio = (this.fence.max - this.fence.min) / fullSize, offsetRatio = (this.fence.min - this.options.min) / fullSize;
        this.fenceEl = createEl('div', { [alias]: 'fence', style: `${this.propsMap.size}:${sizeRatio * 100}%;${this.propsMap.position}:${offsetRatio * 100}%` });
        this.rulerEl.appendChild(this.fenceEl);
    }
    setMinorTicks(offsetRatio) {
        let count = 0, unitRatio = 0, ceilSize = this.sizes.ruler / this.options.ruler.majorEqual;
        if (this.options.ruler.minorEqual === 'auto') {
            count = ceilSize < 40 ? 5 :
                (ceilSize >= 40 && ceilSize < 80) ? 6 :
                    (ceilSize >= 80 && ceilSize < 160) ? 7 :
                        (ceilSize >= 160 && ceilSize < 320) ? 8 :
                            (ceilSize >= 320 && ceilSize >= 640) ? 9 : 10;
        }
        else if (this.options.ruler.minorEqual > 0) {
            count = this.options.ruler.minorEqual;
        }
        unitRatio = 1 / (count * this.options.ruler.majorEqual);
        for (let k = 0; k < count; k++) {
            this.tickEl.insertAdjacentHTML('beforeend', `<li style="${this.propsMap.position}:${(offsetRatio + unitRatio * k) * 100}%"></li>`);
        }
    }
    disable(cb) {
        this.targetEl.toggleAttribute('inert', true);
        this.inputEl.disabled = true;
        super.listen({ name: 'disabled', cb });
        return this;
    }
    enable(cb) {
        this.targetEl.removeAttribute('inert');
        this.inputEl.disabled = false;
        super.listen({ name: 'enabled', cb });
        return this;
    }
    clear(cb) {
        if (this.destroyed)
            return this;
        this.options.multiple ? this.setVals([this.fence.min, this.fence.max]) : this.setVals(this.fence.min);
        super.listen({ name: 'cleared', cb });
        return this;
    }
    restore(cb) {
        if (this.destroyed)
            return this;
        this.setVals(this.rawVal);
        super.listen({ name: 'restored', cb, params: [this.rawVal] });
        return this;
    }
    destroy(cb) {
        if (this.destroyed)
            return this;
        this.resizeObs.disconnect();
        for (let k in this.gestures)
            this.gestures[k]?.destroy();
        if (this.options.keyboard) {
            for (let k in this.handles) {
                let tmp = this.handles[k];
                if (tmp) {
                    tmp.removeEventListener('keydown', this.keyEvt);
                    tmp.removeEventListener('pointerup', this.focusEvt);
                }
            }
        }
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

let AXTMP_placeholder = config.lang.form.placeholder;
const optDatetime = [
    {
        attr: 'name',
        prop: 'name',
        value: ''
    },
    {
        attr: 'value',
        prop: 'value',
        value: ''
    },
    {
        attr: 'disabled',
        prop: 'disabled',
        value: false
    }, {
        attr: 'size',
        prop: 'size',
        value: ''
    },
    {
        attr: 'format',
        prop: 'format',
        value: '',
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'mode',
        prop: 'mode',
        value: 'single',
    },
    {
        attr: 'feature',
        prop: 'feature',
        value: 'single',
    },
    {
        attr: 'multiline',
        prop: 'multiline',
        value: false,
    }, {
        attr: 'placeholder',
        prop: 'placeholder',
        value: AXTMP_placeholder,
    },
    {
        attr: 'label',
        prop: 'label',
        value: '',
    },
    {
        attr: 'tools',
        prop: 'tools',
        value: {
            enable: false,
            children: ['close'],
        },
    },
    {
        attr: 'max-selection',
        prop: 'maxSelection',
        value: 100,
    },
    {
        attr: 'rows',
        prop: 'rows',
        value: 1,
    },
    {
        attr: 'cols',
        prop: 'cols',
        value: 1,
    },
    {
        attr: 'min-date',
        prop: 'minDate',
        value: '',
    },
    {
        attr: 'max-date',
        prop: 'maxDate',
        value: '',
    },
    {
        attr: 'datespan',
        prop: 'datespan',
        value: [],
    },
    {
        attr: 'timespan',
        prop: 'timespan',
        value: [],
    },
    {
        attr: 'full',
        prop: 'full',
        value: false,
    },
    {
        attr: 'footer',
        prop: 'footer',
        value: {
            enable: true,
            layout: 'plain',
            children: ['clear', 'confirm'],
        },
    },
    {
        attr: 'now-hide',
        prop: 'nowHide',
        value: false,
    },
    {
        attr: 'now-show',
        prop: 'nowShow',
        value: false,
    },
    {
        attr: 'clear-show',
        prop: 'clearShow',
        value: true,
    },
    {
        attr: 'close-show',
        prop: 'closeShow',
        value: true,
    },
    {
        attr: 'cancel-show',
        prop: 'cancelShow',
        value: false,
    },
    {
        attr: 'result-Show',
        prop: 'resultShow',
        value: false,
    },
    {
        attr: 'confirm-hide',
        prop: 'confirmHide',
        value: true,
    },
    {
        attr: 'auto-fill',
        prop: 'autoFill',
        value: false,
    },
    {
        attr: 'manual',
        prop: 'manual',
        value: false,
    },
    {
        attr: 'auto-correct',
        prop: 'autoCorrect',
        value: true,
    },
    {
        attr: 'required',
        prop: 'required',
        value: false,
    },
    {
        attr: 'week-start',
        prop: 'weekStart',
        value: 'monday',
    },
    {
        attr: 'separator',
        prop: 'separator',
        value: ',',
    },
    {
        attr: 'btn-sel',
        prop: 'btnSel',
        value: '',
    },
    {
        attr: 'pos-sel',
        prop: 'posSel',
        value: '',
    },
    {
        attr: 'input-sel',
        prop: 'inputSel',
        value: '',
    },
    {
        attr: 'child-sel',
        prop: 'childSel',
        value: '',
    },
    {
        attr: 'display',
        prop: 'display',
        value: 'popup',
    },
    {
        attr: 'toDrawer',
        prop: 'toDrawer',
        value: true,
    },
    {
        attr: 'bubble',
        prop: 'bubble',
        value: {},
    },
    {
        attr: 'fill-now',
        prop: 'fillNow',
        value: false,
    },
    {
        attr: 'next',
        prop: 'next',
        value: '',
    },
    {
        attr: 'lunar',
        prop: 'lunar',
        value: {
            enable: false,
            handler: '',
            map: {},
            tplStr: `<div>农历：{{this.animal}}年 {{this.month}}{{this.date}}</div>
                    <div>干支：{{this.gzYear}}年 {{this.gzMonth}}月 {{this.gzDate}}日</div>
                    <div>节日/节气：{{this.term}} {{this.lunarFestival}} {{this.solarFestival}}</div>
                    `,
            tplEng: null,
            arrange: ['solarFestival', 'lunarFestival', 'term', 'date'],
            tags: [],
        }
    },
    {
        attr: 'events',
        prop: 'events',
        value: {
            enable: false,
            display: 'popup',
            tplStr: `<div ${alias}="label">{{this.label}}</div><div ${alias}="content">{{this.content}}</div>`,
            tplEng: null,
            heading: `YYYY年MM月DD日 WW`,
            list: [],
        },
    },
    {
        attr: 'menu',
        prop: 'menu',
        value: [],
    },
    {
        attr: 'b4-fill',
        prop: 'b4fill',
        value: null,
    },
    {
        attr: 'b4-remove',
        prop: 'b4Remove',
        value: null,
    },
    {
        attr: 'b4-set',
        prop: 'b4Set',
        value: null,
    },
    {
        attr: 'b4-clear',
        prop: 'b4Clear',
        value: null,
    },
    {
        attr: 'b4-cancel',
        prop: 'b4Cancel',
        value: null,
    },
    {
        attr: 'b4-confirm',
        prop: 'b4Confirm',
        value: null,
    },
    {
        attr: 'on-filled',
        prop: 'onFilled',
        value: null,
    },
    {
        attr: 'on-removed',
        prop: 'onRemoved',
        value: null,
    },
    {
        attr: 'on-set',
        prop: 'onSet',
        value: null,
    },
    {
        attr: 'on-setrange',
        prop: 'onSetRange',
        value: null,
    },
    {
        attr: 'on-createdpanels',
        prop: 'onCreatedPanels',
        value: null,
    },
    {
        attr: 'on-canceled',
        prop: 'onCanceled',
        value: null,
    },
    {
        attr: 'on-confirmed',
        prop: 'onConfirmed',
        value: null,
    }, {
        attr: 'on-cleareded',
        prop: 'onCleared',
        value: null,
    },
    {
        attr: 'on-opened',
        prop: 'onOpened',
        value: null,
    },
    {
        attr: 'on-closed',
        prop: 'onClosed',
        value: null,
    },
    {
        attr: 'on-transferred',
        prop: 'onTransferred',
        value: null,
    },
    {
        attr: 'on-updatedtime',
        prop: 'onUpdatedTime',
        value: null,
    },
    ...optBase
];

class Datetime extends ModBaseListenCache {
    options = {};
    value;
    raw;
    client;
    output;
    data;
    format;
    spreadFooter;
    separator;
    selectedsProxy;
    selecteds;
    resultIns;
    bubbleEl;
    bubbleIns;
    canClick;
    inputEl;
    positionEl;
    childEl;
    monthStr;
    panelIdx;
    datespan;
    timespan;
    wrapEl;
    menuEl;
    resultEl;
    outerEl;
    prevEl;
    nextEl;
    mainEl;
    panelsEl;
    footEl;
    bubbleFootEl;
    footBtnsEl;
    footTipsEl;
    confirmEl;
    clearEl;
    closeEl;
    cancelEl;
    nowEl;
    daytimeBtnEl;
    yearSearchEl;
    rangeChkEl;
    detailEl;
    headingEl;
    eventEl;
    lunarEl;
    detailIns;
    timePanelsEl;
    prevYearEl;
    prevMonthEl;
    nextYearEl;
    nextMonthEl;
    static hostType = 'node';
    static optMaps = optDatetime;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            maps: Datetime.optMaps,
            host: elem,
            component: true,
            spread: ['lunar', 'events', 'footer', 'manual', 'tools']
        });
        
        this.value = [];
        this.raw = [];
        this.monthStr = repeatStr(`<li><span ${alias}="content"><i ${alias}="label">{{this.data[this.index-1]}}</i></span></li>`, 12, this.options.lang.month);
        this.client = getClientType();
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.output = { value: '', raw: '', items: [] };
        this.data = {
            startDate: '',
            selecteds: [],
            type: '',
            panels: [
            
            ],
            menu: [],
            year: {
                panelEl: '',
                listEl: '',
                list: []
            },
            
            month: {
                panelEl: '',
                listEl: '',
                list: []
            },
            
            daytime: [],
            
        };
        this.setFeature();
        this.format = trim(this.options.format || 'YYYY/MM/DD');
        this.data.type = dateTools.getDateType(this.format);
        this.getDateTimeSpan();
        this.options.mode === 'range' && this.options.cols === 1 && (this.options.cols = 2);
        if (this.client === 'phone') {
            this.options.display !== 'inline' && this.options.toDrawer && (this.options.display = 'drawer');
            if (this.options.lunar.enable) {
                this.options.cols = 1;
                this.options.rows = 1;
            }
            else {
                if (this.options.cols > 1) {
                    this.options.cols = 1;
                    this.options.rows = 2;
                }
            }
            this.spreadFooter = false;
        }
        else {
            this.spreadFooter = (this.data.type !== 'daytime' && this.options.cols > 1) || (this.data.type === 'daytime' && this.options.mode === 'range');
        }
        this.separator = this.options.mode === 'range' ? (this.options.lang.range.hyphen || this.options.separator) : this.options.separator;
        this.getTargets();
        this.selectedsProxy = new Observe(this.data.selecteds, {
            deep: true,
        });
        this.selecteds = this.selectedsProxy.proxy;
        this.selectedsProxy.on('completed', (data) => {
            if (this.resultEl) {
                let newContent = data.proxy.map((k) => {
                    return { label: dateTools.fillFormat(k, this.format, this.options.lang), source: k };
                });
                this.resultIns.update({
                    content: newContent,
                });
            }
            this.options.autoFill && this.confirm(false);
        });
        (this.data.type !== 'daytime') && this.handleDayCont();
        this.createSkeleton();
        (this.data.type !== 'daytime') ? this.createPanels() : this.handleTimeCont();
        if (this.options.display === 'inline') {
            if (this.targetEl) {
                this.targetEl.insertAdjacentElement('afterend', this.bubbleEl);
                this.bubbleEl.insertAdjacentHTML('beforebegin', `<div class="${prefix}datetime-br"></div>`);
                this.bubbleEl.insertAdjacentHTML('afterend', `<div class="${prefix}datetime-br"></div>`);
            }
        }
        else {
            if (this.options.display === 'dialog') {
                this.bubbleIns = this.bubbleIns || new Dialog(this.targetEl, extend({
                    target: {
                        content: this.bubbleEl,
                        size: '',
                        padding: false,
                        tools: true,
                        footer: { enable: false },
                        wing: { selector: this.options.btnSel },
                        canClick: this.canClick,
                    },
                    source: this.options.bubble
                }));
            }
            else if (this.options.display === 'drawer') {
                this.options.full = true;
                this.bubbleIns = this.bubbleIns || new Drawer(this.targetEl, extend({
                    target: {
                        content: this.bubbleEl,
                        size: '',
                        padding: false,
                        tools: true,
                        footer: { enable: false },
                        placement: 'bottom',
                        wing: { selector: this.options.btnSel },
                        canClick: this.canClick,
                    },
                    source: this.options.bubble,
                }));
            }
            else {
                this.bubbleIns = this.bubbleIns || new Popup(this.positionEl, extend({
                    target: {
                        content: this.bubbleEl,
                        trigger: 'click',
                        tools: { enable: true, placement: 'outside' },
                        size: '',
                        padding: false,
                        placement: 'bottom-start',
                        footer: { enable: false },
                        wing: { selector: this.options.btnSel },
                        canClick: this.canClick,
                    },
                    source: this.options.bubble
                }));
            }
        }
        if (this.bubbleIns) {
            this.bubbleIns.on('show', () => {
                this.options.autoCorrect && this.checkB4Show();
            });
            this.bubbleIns.on('shown', () => {
                super.listen({ name: 'shown' });
            });
        }
        this.setAttrs();
        this.renderFinish();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    setFeature() {
        if (this.options.feature === 'static') {
            this.options.footer = false;
        }
        else if (this.options.feature === 'flat') {
            this.options.footer = false;
            this.options.autoFill = true;
            this.options.display = 'inline';
        }
    }
    getTargets() {
        if (this.targetEl && this.targetEl.classList.contains(`${prefix}datetime-wrap`)) {
            this.targetEl.innerHTML = '';
            this.inputEl = createEl('ax-textarea', { mean: `${prefix}icon-calendar`, placeholder: this.options.placeholder || config.lang.form.placeholder });
            this.options.name && this.inputEl.setAttribute('name', this.options.name);
            this.options.label && this.inputEl.setAttribute('label', this.options.label);
            if (this.options.tools.enable && !isEmpty(this.options.tools.children)) {
                this.inputEl.propsProxy.tools = this.options.tools.children;
                this.inputEl.changedTools();
                this.canClick = (el) => (this.inputEl.toolsEl && this.inputEl.toolsEl.contains(el)) || (this.inputEl.labelEl && this.inputEl.labelEl.contains(el)) ? false : true;
            }
            this.inputEl.toggleAttribute('single', !this.options.multiline);
            this.rawHtml.trim() && this.inputEl.setAttribute('value', this.rawHtml.trim());
            this.options.size ? this.inputEl.setAttribute('size', this.options.size) : this.inputEl.removeAttribute('size');
            this.targetEl.appendChild(this.inputEl);
            this.positionEl = this.targetEl;
        }
        else {
            this.positionEl = getEl(this.options.posSel) || this.targetEl;
            this.inputEl = getEl(this.options.inputSel) || this.targetEl;
            this.inputEl && this.inputEl.classList.add(`${prefix}bind-datetime`);
            this.childEl = getEl(this.options.childSel, this.inputEl);
        }
    }
    setAttrs() {
        this.options.classes && this.targetEl && classes(this.targetEl).add(this.options.classes);
        this.bubbleEl.setAttribute('mode', this.options.mode);
        this.data.type ? this.bubbleEl.setAttribute('type', this.data.type) : this.bubbleEl.removeAttribute('type');
        this.bubbleEl.setAttribute('display', this.options.display);
        this.options.feature ? this.bubbleEl.setAttribute('feature', this.options.feature) : this.bubbleEl.removeAttribute('feature');
        if (this.inputEl) {
            this.inputEl.toggleAttribute('readonly', true);
            this.inputEl.toggleAttribute('disabled', this.options.disabled);
        }
        this.bubbleEl.toggleAttribute('disabled', this.options.disabled);
        this.bubbleEl.toggleAttribute('full', this.options.full);
        this.bubbleEl.setAttribute('footer', this.options.footer.enable);
        this.bubbleEl.toggleAttribute('lunar', this.options.lunar.enable);
    }
    createPanel(index, dateObj) {
        let curDate = dateTools.getDate(dateObj, this.data.type, this.options.lang), panelObj = {}, getWeek = () => {
            let tpl = (this.options.weekStart === 'monday') ? `<li>{{this.data[this.index-1]}}</li>` : `<li>{{this.data[this.index===1?6:this.index-2]}}</li>`;
            return repeatStr(tpl, 7, this.options.lang.week);
        };
        if (this.data.type.includes('date')) {
            curDate.setMonth(curDate.getMonth() + index);
            panelObj.month = curDate.getMonth();
            panelObj.year = curDate.getFullYear();
            panelObj.panelEl = tplToEl(`
                        <li panel='${this.options.lunar.enable ? 'lunar' : 'date'}'>
                            <div ${alias}="head">
                                <span ${alias}="year"><i>${panelObj.year}</i>${this.options.lang.unit.Y}</span><span ${alias}="month"><i>${panelObj.month + 1}</i>${this.options.lang.unit.M}</span>
                            </div>
                            <div ${alias}="body" days>
                                <ul ${alias}="column" class="${prefix}reset  ${prefix}grid ${prefix}avg-7">
                                    ${getWeek()}
                                </ul>
                                <ul ${alias}="list" class="${prefix}reset ${prefix}grid ${prefix}avg-7"></ul>
                            </div>
                        </li>
                        `);
            panelObj.type = 'date';
            panelObj.headEl = panelObj.panelEl.querySelector(`[${alias}="head"]`);
            panelObj.yearEl = panelObj.headEl.querySelector(`[${alias}="year"]`);
            panelObj.monthEl = panelObj.headEl.querySelector(`[${alias}="month"]`);
            panelObj.listEl = panelObj.panelEl.querySelector(`[${alias}="list"]`);
            panelObj.list = this.createPanelDayList(panelObj.listEl, curDate);
        }
        else if (this.data.type === 'month') {
            curDate.setFullYear(curDate.getFullYear() + index);
            panelObj.year = curDate.getFullYear();
            panelObj.panelEl = tplToEl(`
                    <li panel='month'>
                        <div ${alias}="head">
                            <span ${alias}="year"><i>${panelObj.year}</i>${this.options.lang.unit.Y}</span>
                        </div>
                        <div body>
                            <ul ${alias}="list" class="${prefix}reset ${prefix}grid ${prefix}avg-3">${this.monthStr}</ul>
                        </div>
                    </li>
                    `);
            panelObj.type = 'month';
            panelObj.headEl = panelObj.panelEl.querySelector(`[${alias}="head"]`);
            panelObj.yearEl = panelObj.headEl.querySelector(`[${alias}="year"]`);
            panelObj.listEl = panelObj.panelEl.querySelector(`[${alias}="list"]`);
            panelObj.list = this.createPanelMonthList(panelObj.listEl, curDate);
        }
        else if (this.data.type === 'year') {
            curDate.setFullYear(curDate.getFullYear() + 18 * index - 7);
            panelObj.year = curDate.getFullYear();
            panelObj.panelEl = tplToEl(`
                <li panel='year'>
                    <div ${alias}="head">
                        <span ${alias}="year"><i>${panelObj.year}</i>${this.options.lang.unit.Y}~<i>${panelObj.year + 17}</i>${this.options.lang.unit.Y}</span>
                    </div>
                    <div ${alias}="body">
                        <ul ${alias}="list" class="${prefix}reset ${prefix}grid ${prefix}avg-3"></ul>
                    </div>
                </li>
                `);
            panelObj.type = 'year';
            panelObj.headEl = panelObj.panelEl.querySelector(`[${alias}="head"]`);
            panelObj.listEl = panelObj.panelEl.querySelector(`[${alias}="list"]`);
            panelObj.list = this.createPanelYearList(panelObj.listEl, curDate);
        }
        panelObj.bodyEl = panelObj.panelEl.querySelector(`[${alias}="body"]`);
        this.data.panels.push(panelObj);
        this.panelsEl.appendChild(panelObj.panelEl);
    }
    createPanels(dateObj, force = false, cb) {
        if (this.destroyed)
            return;
        let index = this.options.rows * this.options.cols, startDate = dateTools.getDate(dateObj || this.data.startDate, this.data.type, this.options.lang);
        if (!force && this.inCurPanels(startDate))
            return;
        this.data.panels = [];
        this.panelsEl.innerHTML = '';
        for (let k = 0; k < index; k++) {
            this.createPanel(k, startDate);
        }
        this.data.startDate = startDate;
        let curItems = this.getCurGrids();
        curItems.forEach((k) => {
            this.inSelectedDate(k) && !k.selected && this.selectedAttrToggle(k, true);
        });
        this.options.mode === 'range' && this.setRangeAttr(curItems);
        let nowObj = dateTools.getDateObj(new Date(), this.data.type), nowItem = curItems.find((k) => JSON.stringify(k.value) == JSON.stringify(nowObj));
        nowItem && nowItem.wrapEl.setAttribute('now', '');
        this.renderFinish();
        super.listen({ name: 'createPanels', cb });
        return this;
    }
    inSelectedDate(data) {
        let value = this.getDateVal(data, this.data.type);
        return this.selecteds.some((k) => {
            let { hh, mm, ss, ...rest } = k;
            return JSON.stringify(rest) == JSON.stringify(value);
        });
    }
    showEvent(obj) {
        this.headingEl.innerHTML = dateTools.fillFormat(obj.value, this.options.events.heading, this.options.lang);
        if (this.options.lunar.enable && this.options.lunar.handler) {
            let lunarObj = { ...this.options.lunar.map }, lunarVal = this.options.lunar.handler(obj.value.YYYY, obj.value.MM + 1, obj.value.DD);
            for (let k in lunarObj) {
                if (lunarObj.hasOwnProperty(k)) {
                    lunarObj[k] = lunarVal[lunarObj[k]];
                }
            }
            this.lunarEl.innerHTML = super.getTplcont(lunarObj, this.options.lunar.tplStr, this.options.lunar.tplEng);
        }
        let item = this.options.events.list.find((k) => dateTools.isSameDay(dateTools.getDateObj(k.date, 'date'), dateTools.getDateObj(obj.value, 'date')));
        this.eventEl.innerHTML = item && item.content ? super.getTplcont(item, this.options.events.tplStr, this.options.events.tplEng) : this.options.lang.noEvent;
    }
    inCurPanels(dateObj, curGrids) {
        let type = this.data.type === 'datetime' ? 'date' : this.data.type, date = getDataType(dateObj) === 'Object' ? { ...dateObj } : dateObj, obj = dateTools.getDateObj(date, type), grids = curGrids || this.getCurGrids(), result;
        result = grids.some((k) => JSON.stringify(k.value) == JSON.stringify(obj));
        return result;
    }
    inputVal2Dates(toDate = true) {
        let str = this.getValFromTgt();
        return this.str2Dates(str, toDate);
    }
    str2Dates(str, toDate = true) {
        if (str) {
            return str.split(this.separator).map((k) => toDate ? dateTools.getDate(k, this.data.type, this.options.lang) : k);
        }
        else {
            return [];
        }
    }
    getValFromTgt() {
        if (!this.inputEl)
            return '';
        return ('value' in this.inputEl ? this.inputEl.value : this.childEl ? this.childEl.textContent : this.inputEl.textContent).trim();
    }
    setValFromTgt(value) {
        value = value || '';
        if ('value' in this.inputEl) {
            this.inputEl.nodeName === 'TEXTAREA' ? this.inputEl.value = value : this.inputEl.setAttribute('value', value);
        }
        else {
            this.childEl ? this.childEl.innerHTML = value : this.inputEl.innerHTML = value;
        }
    }
    vals2Arr() {
        let type = getDataType(this.options.value), result = [];
        if (!isEmpty(this.options.value)) {
            if (type === 'Array') {
                result = this.options.value.map((k) => dateTools.getDate(k, this.data.type, this.options.lang));
            }
            else if (type === 'String') {
                result = this.str2Dates(this.options.value, true);
            }
            else {
                result = [dateTools.getDate(this.options.value, this.data.type, this.options.lang)];
            }
        }
        else {
            if (this.options.autoCorrect) {
                let value = this.getValFromTgt();
                if (value) {
                    result = this.inputVal2Dates();
                }
                else {
                    result = this.options.fillNow ? [dateTools.getDate(new Date(), this.data.type, this.options.lang)] : [];
                }
            }
            else {
                result = [];
            }
        }
        return result;
    }
    handleDayCont() {
        this.value = this.vals2Arr();
        if (this.value.length > 1 && this.options.mode === 'range') {
            dateTools.sort(this.value);
            this.value.length > 2 && (this.value = this.value.slice(0, 2));
        }
        this.data.startDate = this.value.length === 0 ? dateTools.getDate(new Date(), this.data.type, this.options.lang) : this.value[0];
        let selectedContent = this.dates2Objs(this.value);
        this.selecteds.splice(0, this.data.selecteds.length, ...selectedContent);
        this.raw = [...selectedContent];
        if (this.inputEl) {
            let value = this.selecteds.map((k) => dateTools.fillFormat(k, this.format, this.options.lang)).join(this.separator);
            this.handleVal(value);
        }
    }
    stdTimeStr(data, type = 'object') {
        let objArr = [], dataFn = (obj) => dateTools.getDateObj(obj, 'daytime');
        if (Array.isArray(data)) {
            for (let k of data)
                objArr.push(dataFn(k));
        }
        else {
            objArr.push(dataFn(data));
        }
        return (type === 'string') ? objArr.map((k) => `${dateTools.fillZero(k.hh)}:${dateTools.fillZero(k.mm)}:${dateTools.fillZero(k.ss)}`) : objArr;
    }
    handleTimeCont() {
        this.value = this.stdTimeStr(this.vals2Arr());
        if (this.options.mode === 'range') {
            if (this.value.length === 0) {
                this.setDaytime([{ hh: 0, mm: 0, ss: 0 }, { hh: 0, mm: 0, ss: 0 }]);
            }
            else if (this.value.length === 1) {
                this.setDaytime([this.value[0], { hh: 0, mm: 0, ss: 0 }]);
            }
            else {
                this.setDaytime(this.value);
            }
        }
        else {
            if (this.value.length === 0) {
                this.setDaytime({ hh: 0, mm: 0, ss: 0 });
            }
            else {
                this.setDaytime(this.value[0]);
            }
        }
        this.raw = [...this.value];
        if (this.inputEl && !isEmpty(this.options.value)) {
            let value = this.stdTimeStr(this.value, 'string').join(this.separator);
            this.fillVals(value);
        }
    }
    dates2Objs(dates) {
        let selectedContent = [];
        if (this.data.type === 'year') {
            selectedContent = dates.map((k) => {
                return { YYYY: k.getFullYear() };
            });
        }
        else if (this.data.type === 'month') {
            selectedContent = dates.map((k) => {
                return { YYYY: k.getFullYear(), MM: k.getMonth() };
            });
        }
        else if (this.data.type === 'daytime') {
            selectedContent = dates.map((k) => {
                return { hh: k.getHours(), mm: k.getMinutes(), ss: k.getSeconds() };
            });
        }
        else if (this.data.type === 'date') {
            selectedContent = dates.map((k) => {
                return { YYYY: k.getFullYear(), MM: k.getMonth(), DD: k.getDate() };
            });
        }
        else if (this.data.type === 'datetime') {
            selectedContent = dates.map((k) => {
                return { YYYY: k.getFullYear(), MM: k.getMonth(), DD: k.getDate(), hh: k.getHours(), mm: k.getMinutes(), ss: k.getSeconds() };
            });
        }
        return selectedContent;
    }
    checkB4Show() {
        if (this.data.type !== 'daytime') {
            let value = this.inputVal2Dates(), selecteds = this.dates2Objs(value), isSame = JSON.stringify(selecteds) == JSON.stringify(this.data.selecteds);
            if (!isSame) {
                this.data.startDate = value.length > 0 ? value[0] : dateTools.getDate(this.data.selecteds[0], this.data.type, this.options.lang);
                this.createPanels();
                this.clear();
                this.setVals(selecteds);
            }
        }
        else {
            let timeObj = this.stdTimeStr(this.inputVal2Dates());
            this.setDaytime(timeObj);
        }
    }
    createSkeleton() {
        let outerHTML = '', footHTML = '';
        if (this.data.type !== 'daytime') {
            outerHTML = `
            <div ${alias}="outer">
                        <div ${alias}="prev"></div>
                            <div ${alias}="next"></div>
                            <ul ${alias}="panels" class="${prefix}reset ${prefix}grid ${this.client === 'phone' ? prefix + 'avg-1' : this.spreadFooter ? prefix + 'fence ' + prefix + 'avg-' + this.options.cols : prefix + 'avg-1'}" ></ul>
                        </div>`;
        }
        if (this.options.footer.enable) {
            footHTML = `<div ${alias}="foot"></div>`;
        }
        this.bubbleEl = tplToEl(`
                <div class="${prefix}datetime">
                    <div ${alias}="wrap">
                        ${this.options.menu.length > 0 ? '<ul ' + alias + '="menu"  class="' + prefix + 'reset"></ul>' : ''}
                        <div ${alias}="main">
                            ${this.options.resultShow && this.data.type !== 'daytime' ? '<div ' + alias + '="result"></div>' : ''}
                            ${outerHTML}
                            ${footHTML}
                        </div>
                    </div>
                </div>
            `);
        this.wrapEl = this.bubbleEl.querySelector(`[${alias}="wrap"]`);
        this.menuEl = this.bubbleEl.querySelector(`[${alias}="menu"]`);
        this.resultEl = this.bubbleEl.querySelector(`[${alias}="result"]`);
        this.outerEl = this.bubbleEl.querySelector(`[${alias}="outer"]`);
        this.prevEl = this.bubbleEl.querySelector(`[${alias}="prev"]`);
        this.nextEl = this.bubbleEl.querySelector(`[${alias}="next"]`);
        this.mainEl = this.bubbleEl.querySelector(`[${alias}="main"]`);
        this.panelsEl = this.mainEl.querySelector(`[${alias}="panels"]`);
        if (this.options.footer.enable) {
            this.footEl = this.bubbleEl.querySelector(`[${alias}="foot"]`);
            if (this.spreadFooter) {
                this.bubbleFootEl = createFooter(Object.assign(this.options.footer, { layout: 'right' }), this.footEl, this);
                this.footBtnsEl = this.bubbleFootEl.querySelector(`.${prefix}box-btns`);
                let tmp = this.bubbleFootEl.querySelector(`[${alias}="tips"]`);
                if (tmp) {
                    this.footTipsEl = tmp;
                }
                else {
                    this.footTipsEl = createEl('div', { [alias]: 'tips' });
                    this.footBtnsEl.insertAdjacentElement('afterend', this.footTipsEl);
                }
            }
            else {
                this.bubbleFootEl = createFooter(Object.assign(this.options.footer, { layout: 'plain' }), this.footEl, this);
                this.footBtnsEl = this.bubbleFootEl.querySelector(`.${prefix}box-btns`);
                this.footTipsEl = this.footBtnsEl;
            }
            this.confirmEl = this.bubbleFootEl.querySelector(`[${alias}="confirm"]`);
            this.clearEl = this.bubbleFootEl.querySelector(`[${alias}="clear"]`);
            this.closeEl = this.bubbleFootEl.querySelector(`[${alias}="close"]`);
            this.cancelEl = this.bubbleFootEl.querySelector(`[${alias}="cancel"]`);
            this.nowEl = this.bubbleFootEl.querySelector(`[${alias}="now"]`);
            this.daytimeBtnEl = this.data.type.includes('time') ? createEl('ax-btn', { type: 'plain', [alias]: 'daytime', label: this.options.lang.daytime.select }) : null;
            this.yearSearchEl = this.data.type === 'year' ? createEl('ax-input', { [alias]: 'search', placeholder: this.options.lang.year.placeholder, btn: `<i class="${prefix}icon-arrow-right"></i>` }) : null;
            this.rangeChkEl = this.options.mode === 'range' && this.data.type === 'datetime' ? createEl('ax-checkbox', { disabled: '', [alias]: 'checkbox', label: this.options.lang.range.checkbox }) : null;
            this.daytimeBtnEl && this.footTipsEl.insertAdjacentElement('afterbegin', this.daytimeBtnEl);
            this.yearSearchEl && this.footTipsEl.insertAdjacentElement('afterbegin', this.yearSearchEl);
            if (this.rangeChkEl) {
                this.footTipsEl.insertAdjacentElement('afterbegin', this.rangeChkEl);
                this.rangeChkEl.on('check', (data) => {
                    if (data.checked) {
                        if (this.selecteds.length === 1) {
                            this.selecteds.push({ ...this.selecteds[0] });
                            this.setRange(this.selecteds);
                        }
                    }
                    else {
                        if (this.selecteds.length === 2 && dateTools.isSameDay(this.selecteds[0], this.selecteds[1])) {
                            let tmp = { ...this.selecteds[0] };
                            this.clear(false);
                            this.select(tmp);
                        }
                    }
                });
            }
        }
        if (this.options.events.enable) {
            this.detailEl = createEl('div', { [alias]: 'detail' });
            this.headingEl = createEl('div', { [alias]: 'heading' });
            this.eventEl = createEl('div', { [alias]: 'event' }, this.options.lang.noEvent);
            this.detailEl.appendChild(this.headingEl);
            this.detailEl.appendChild(this.eventEl);
            if (this.options.lunar.enable && this.options.lunar.handler) {
                this.lunarEl = createEl('div', { [alias]: 'lunar' });
                this.headingEl.insertAdjacentElement('afterend', this.lunarEl);
            }
            if (this.options.events.display === 'inline') {
                this.wrapEl.appendChild(this.detailEl);
            }
            else if (this.options.events.display === 'popup') {
                this.detailIns = this.detailIns || new Popup(this.panelsEl, {
                    content: this.detailEl,
                    trigger: 'click',
                    placement: 'bottom',
                    size: 'md',
                    footer: false,
                });
            }
        }
        this.data.type.includes('time') && this.options.footer.enable && this.createTimeSelectPanel();
        this.createMenu();
        if (this.data.type !== 'daytime') {
            if (this.resultEl) {
                this.resultIns = this.resultIns || new Tags(this.resultEl, {
                    content: '',
                    removable: true,
                    empty: { enable: true, content: this.options.lang.empty },
                });
            }
            if (this.data.type === 'year' || this.data.type === 'month') {
                this.prevEl.innerHTML = `<i ${alias}="prevYear" class= "${prefix}icon-left-double"></i> `;
                this.nextEl.innerHTML = `<i ${alias}="nextYear" class= "${prefix}icon-right-double"></i> `;
            }
            else if (this.data.type.includes('date')) {
                this.prevEl.innerHTML = `<i ${alias}="prevYear" class= "${prefix}icon-left-double"></i> <i ${alias}="prevMonth" class="${prefix}icon-left"></i>`;
                this.nextEl.innerHTML = `<i ${alias}="nextYear" class= "${prefix}icon-right-double"></i> <i ${alias}="nextMonth" class="${prefix}icon-right"></i>`;
            }
            this.prevYearEl = this.prevEl.querySelector(`[${alias}="prevYear"]`);
            this.prevMonthEl = this.prevEl.querySelector(`[${alias}="prevMonth"]`);
            this.nextYearEl = this.mainEl.querySelector(`[${alias}="nextYear"]`);
            this.nextMonthEl = this.mainEl.querySelector(`[${alias}="nextMonth"]`);
            if (this.data.type === 'month' || this.data.type.includes('date')) {
                this.createYearPanel();
            }
            if (this.data.type.includes('date')) {
                this.createMonthPanel();
            }
        }
    }
    createMenu() {
        if (this.options.menu.length > 0) {
            this.data.menu = this.options.menu;
            let fragment = document.createDocumentFragment();
            this.data.menu.forEach((k) => {
                k.el = createEl('li', '', k.label);
                fragment.appendChild(k.el);
            });
            this.menuEl.appendChild(fragment);
            this.data.menu.forEach((k) => {
                k.el.onclick = () => {
                    k.action && k.action.call(this, k);
                    k.el.setAttribute('selected', '');
                    k.selected = true;
                    this.data.menu.filter((i) => i !== k).forEach((o) => {
                        Reflect.deleteProperty(o, 'selected');
                        o.el.removeAttribute('selected');
                    });
                };
            });
        }
    }
    createMonthPanel(cb) {
        this.data.month.panelEl = createEl('div', { panel: 'month' }, `<div ${alias}="body"><ul ${alias}="list" class="${prefix}reset ${prefix}grid ${prefix}avg-3">${this.monthStr}</ul></div>`);
        this.data.month.listEl = this.data.month.panelEl.querySelector(`[${alias}="list"]`);
        [...this.data.month.listEl.children].forEach((k, i) => {
            let obj = {
                wrapEl: k,
                contEl: k.querySelector(`[${alias}="content"]`),
                value: i,
            };
            this.data.month.list.push(obj);
        });
        this.panelsEl.insertAdjacentElement('afterend', this.data.month.panelEl);
        cb && cb();
    }
    createYearPanel(cb) {
        this.data.year.panelEl = createEl('div', { panel: 'year' }, `
            <div ${alias}="body">
                                <ul ${alias}="list" class="${prefix}reset ${prefix}grid ${prefix}avg-3"></ul>
                                <ul ${alias}="pages"  class="${prefix}reset ${prefix}grid ${prefix}avg-3">
                                    <li><i ${alias}="prev">${this.options.lang.year.prev}</i></li>
                                    <li><ax-input placeholder="${this.options.lang.year.placeholder}" size="sm" btn="<i class='${prefix}icon-arrow-right'></i>"/></ax-input></li>
                                    <li><i ${alias}="next">${this.options.lang.year.next}</i></li>
                                </ul>
                            </div>
            `);
        this.data.year.listEl = this.data.year.panelEl.querySelector(`[${alias}="list"]`);
        this.data.year.prevEl = this.data.year.panelEl.querySelector(`[${alias}="prev"]`);
        this.data.year.nextEl = this.data.year.panelEl.querySelector(`[${alias}="next"]`);
        this.data.year.searchEl = this.data.year.panelEl.querySelector('ax-input');
        this.data.year.inputEl = this.data.year.searchEl.inputEl;
        this.data.year.btnEl = this.data.year.searchEl.btnEl;
        this.panelsEl.insertAdjacentElement('afterend', this.data.year.panelEl);
        cb && cb();
    }
    createYearList(panel, start, index) {
        this.data.year.listEl.innerHTML = '';
        this.data.year.list = [];
        this.panelIdx = index || 0;
        for (let k = 0; k < 15; k++) {
            let year = start || start === 0 ? start + k : panel.year - 7 + k, obj = {
                wrapEl: createEl('li', '', `<span ${alias}="content"> <i ${alias}="label">${year}${this.options.lang.unit.Y}</i></span> `),
                value: year,
            };
            obj.contEl = obj.wrapEl.querySelector(`[${alias}="content"]`);
            obj.labelEl = obj.wrapEl.querySelector(`[${alias}="label"]`);
            this.data.year.listEl.appendChild(obj.wrapEl);
            this.data.year.list.push(obj);
        }
        this.data.year.list.forEach((k) => {
            if (panel.year === k.value) {
                this.selectedAttrToggle(k, true);
            }
            else {
                this.selectedAttrToggle(k, false);
            }
            k.contEl.onclick = () => {
                if (k.disabled)
                    return;
                if (panel.year !== k.value) {
                    let startValue = k.value;
                    if (this.data.type === 'month') {
                        startValue = k.value - this.panelIdx;
                    }
                    else if (this.data.type.includes('date')) {
                        startValue = k.value - (this.data.panels[this.panelIdx].year - this.data.panels[0].year);
                    }
                    this.data.startDate.setFullYear(startValue);
                    this.createPanels(null, true);
                    hide({ el: this.data.year.panelEl });
                }
            };
        });
    }
    goto(dateObj, select = false, cb) {
        if (this.destroyed)
            return;
        let date = dateTools.getDate(dateObj, this.data.type, this.options.lang);
        if (select) {
            this.setVals(date);
        }
        else {
            this.createPanels(date);
        }
        cb && cb.call(this);
    }
    offset(dateObj, cb) {
        if (this.destroyed)
            return;
        if (this.data.selecteds.length > 0) {
            let dates = this.data.selecteds.map((k) => dateTools.getDateObj(dateTools.getOffsetDate(k, dateObj), this.data.type));
            this.setVals(dates);
        }
        else {
            this.data.startDate = dateTools.getOffsetDate(dateTools.getDateObj(new Date(), this.data.type), dateObj);
            this.setVals(this.data.startDate);
        }
        cb && cb.call(this);
    }
    selectedAttrToggle(obj, state) {
        if (state) {
            if (!obj.selected) {
                Reflect.set(obj, 'selected', true);
                obj.wrapEl.setAttribute('selected', '');
            }
        }
        else {
            if (obj.selected) {
                Reflect.deleteProperty(obj, 'selected');
                obj.wrapEl.removeAttribute('selected');
            }
        }
    }
    select(data, curGrid, curGrids) {
        let curObj = this.findFromPanels(data, curGrid, curGrids), curItems = curObj.list, curItem = curObj.target, fillTime = (dateObj) => {
            let obj = {};
            if (this.options.mode === 'range') {
                obj = this.data.type.includes('time') ? { ...dateObj, ...this.data.daytime[this.selecteds.length === 1 ? 1 : 0].value } : dateObj;
            }
            else {
                obj = this.data.daytime.length > 0 && this.data.type.includes('time') ? { ...dateObj, ...this.data.daytime[0].value } : dateObj;
            }
            return obj;
        }, selectedFun = (obj) => {
            if (obj.disabled)
                return;
            let value;
            if (this.data.type.includes('time')) {
                value = (data.hasOwnProperty('hh') && data.hasOwnProperty('mm') && data.hasOwnProperty('ss')) ? data : fillTime(obj.value);
            }
            else {
                value = data.value || data;
            }
            if (this.options.mode === 'range') {
                let length = this.selecteds.length;
                if (length < 2) {
                    this.selecteds.push(value);
                    if (length === 1) {
                        let rangeStart = { ...this.data.selecteds[0] }, rangeEnd = { ...this.data.selecteds[1] };
                        if (dateTools.getDate(rangeStart, this.data.type, this.options.lang) > dateTools.getDate(rangeEnd, this.data.type, this.options.lang)) {
                            this.selecteds.sort((a, b) => dateTools.getDate(a, this.data.type, this.options.lang) - dateTools.getDate(b, this.data.type, this.options.lang));
                            this.assignDaytime();
                        }
                        this.rangeChkEl && this.rangeChkEl.setAttribute('disabled', '');
                        this.rangeChkEl && this.rangeChkEl.setAttribute('check', '');
                        this.setRange(this.selecteds);
                    }
                    else {
                        this.rangeChkEl && this.rangeChkEl.removeAttribute('disabled');
                    }
                }
                else {
                    this.clear(false);
                    this.selecteds.splice(0, this.selecteds.length, value);
                    this.rangeChkEl && this.rangeChkEl.removeAttribute('disabled');
                    this.rangeChkEl && this.rangeChkEl.setAttribute('check', '');
                }
            }
            else {
                if (this.options.mode === 'multiple') {
                    !this.inSelectedDate(value) && this.selecteds.push(value);
                }
                else {
                    if (this.inSelectedDate(value))
                        return;
                    this.clear(false);
                    this.selecteds.splice(0, this.selecteds.length, value);
                }
            }
            this.selectedAttrToggle(obj, true);
        };
        if (!curItems.includes(data) && !curItem) {
            this.createPanels(data.hasOwnProperty('value') ? data.value : data);
            curItem = this.findFromPanels(data).target;
        }
        if (curItem.disabled)
            return;
        !curItem?.selected && selectedFun(curItem);
        this.options.events.enable && curItem && this.showEvent(curItem);
    }
    deselect(dateObj, curGrid, curGrids) {
        if (isEmpty(dateObj))
            return;
        let curItems = curGrids || this.getCurGrids(), curItem = this.findFromPanels(dateObj, curGrid, curItems).target, selecteds = [];
        curItem && this.selectedAttrToggle(curItem, false);
        selecteds = this.data.selecteds.filter((k) => !dateTools.isSameDay(k, dateObj.hasOwnProperty('value') ? dateObj.value : dateObj));
        this.selecteds.splice(0, this.data.selecteds.length, ...selecteds);
        if (this.options.mode === 'range' && this.data.selecteds.length < 2) {
            curItems.forEach((k) => {
                this.removeRangeAttr(k);
            });
        }
    }
    setRangeAttr(grids) {
        if (this.data.selecteds.length !== 2)
            return;
        let curGrids = grids || this.getCurGrids(), type = this.data.type === 'datetime' ? 'date' : this.data.type;
        curGrids.forEach((k) => {
            this.removeRangeAttr(k);
            this.selectedAttrToggle(k, false);
            if (dateTools.getDate(k.value, type, this.options.lang) > dateTools.getDate(this.data.selecteds[0], type, this.options.lang) && dateTools.getDate(k.value, type, this.options.lang) < dateTools.getDate(this.data.selecteds[1], type, this.options.lang)) {
                k.wrapEl.setAttribute('contain', '');
            }
        });
        let gridStart = this.findFromPanels(this.data.selecteds[0], null, curGrids).target, gridEnd = this.findFromPanels(this.data.selecteds[1], null, curGrids).target;
        if (gridStart) {
            gridStart.wrapEl.setAttribute('start', '');
            this.selectedAttrToggle(gridStart, true);
        }
        if (gridEnd) {
            gridEnd.wrapEl.setAttribute('end', '');
            this.selectedAttrToggle(gridEnd, true);
        }
    }
    removeRangeAttr(dateObj) {
        if (dateObj.wrapEl.hasAttribute('start')) {
            dateObj.wrapEl.removeAttribute('start');
        }
        if (dateObj.wrapEl.hasAttribute('end')) {
            dateObj.wrapEl.removeAttribute('end');
        }
        if (dateObj.wrapEl.hasAttribute('contain')) {
            dateObj.wrapEl.removeAttribute('contain');
        }
    }
    getDatespan() {
        this.datespan = [];
        if (isEmpty(this.options.datespan))
            return;
        let spanType = getDataType(this.options.datespan), getVal = (obj) => {
            let objType = getDataType(obj), result = {};
            if (objType !== 'Object') {
                return result;
            }
            if (obj.hasOwnProperty('min')) {
                result.min = {
                    Date: dateTools.getDate(obj.min, this.data.type, this.options.lang),
                    Object: dateTools.getDateObj(obj.min, this.data.type, this.options.lang)
                };
            }
            obj.hasOwnProperty('min') && (result.min = dateTools.getDate(obj.min, this.data.type, this.options.lang));
            obj.hasOwnProperty('max') && (result.max = dateTools.getDate(obj.max, this.data.type, this.options.lang));
            if (obj.hasOwnProperty('max')) {
                result.max = {
                    Date: dateTools.getDate(obj.max, this.data.type, this.options.lang),
                    Object: dateTools.getDateObj(obj.max, this.data.type, this.options.lang)
                };
            }
            return result;
        };
        if (spanType === 'Object') {
            this.datespan.push(getVal(this.options.datespan));
        }
        else if (spanType === 'Array') {
            this.datespan = this.options.datespan.map((k) => getVal(k)).filter((k) => !isEmpty(k));
        }
    }
    getTimespan() {
        this.timespan = [];
        if (isEmpty(this.options.timespan))
            return;
        let spanType = getDataType(this.options.timespan), getVal = (obj) => {
            let objType = getDataType(obj), result = {};
            if (objType !== 'Object') {
                return result;
            }
            obj.hasOwnProperty('min') && (result.min = dateTools.getDateObj(obj.min, this.data.type, this.options.lang));
            obj.hasOwnProperty('max') && (result.max = dateTools.getDateObj(obj.max, this.data.type, this.options.lang));
            return result;
        };
        if (spanType === 'Object') {
            this.timespan.push(getVal(this.options.timespan));
        }
        else if (spanType === 'Array') {
            this.timespan = this.options.timespan.map((k) => getVal(k)).filter((k) => !isEmpty(k));
        }
    }
    getDateTimeSpan() {
        let hander = (type = 'timespan') => {
            if (isEmpty(this.options[type]))
                return [];
            let spanType = getDataType(this.options[type]), dateProp = type === 'datespan' ? 'getDate' : 'getDateObj', getVal = (obj) => {
                let objType = getDataType(obj), result = {};
                if (objType !== 'Object')
                    return result;
                obj.hasOwnProperty('min') && (result.min = dateTools[dateProp](obj.min, this.data.type, this.options.lang));
                obj.hasOwnProperty('max') && (result.max = dateTools[dateProp](obj.max, this.data.type, this.options.lang));
                return result;
            };
            if (spanType === 'Object') {
                return [getVal(this.options[type])];
            }
            else if (spanType === 'Array') {
                return this.options[type].map((k) => getVal(k)).filter((k) => !isEmpty(k));
            }
        };
        this.datespan = hander('datespan');
        this.timespan = hander('timespan');
    }
    setWrapDisabled(obj) {
        obj.disabled = true;
        obj.wrapEl.setAttribute('disabled', '');
    }
    compareVal(data, range, prop) {
        let min = range.hasOwnProperty('min') ? prop ? range.min[prop] : range.min : null, max = range.hasOwnProperty('max') ? prop ? range.max[prop] : range.max : null, result;
        if (isNull(min) && isNull(max)) {
            result = false;
        }
        else if (isNull(min)) {
            result = data <= max;
        }
        else if (isNull(max)) {
            result = data >= min;
        }
        else {
            result = data >= min && data <= max;
        }
        return result;
    }
    setDatespanDisabled(dateObj) {
        if (!this.datespan.length || dateObj.disabled)
            return;
        let condition;
        let valDate = dateTools.getDate(dateObj.value, this.data.type, this.options.lang);
        let tmp = this.datespan.find((k) => {
            
            return this.compareVal(valDate, k);
        });
        condition = tmp ? false : true;
        condition && this.setWrapDisabled(dateObj);
    }
    setTimespanDisabled(timeObj, prop) {
        if (!this.timespan.length || timeObj.disabled)
            return;
        let condition;
        let tmp = this.timespan.find((k) => {
            
            return this.compareVal(timeObj.value, k, prop);
        });
        condition = tmp ? false : true;
        condition && this.setWrapDisabled(timeObj);
    }
    getDateVal(dateObj, type) {
        let objType = getDataType(dateObj), value = {}, result;
        if (objType === 'String' || objType === 'Date') {
            result = dateTools.getDateObj(dateObj, 'date');
        }
        else {
            if (objType === 'Object') {
                if (dateObj.hasOwnProperty('value')) {
                    value = dateObj.value;
                }
                else {
                    value = dateObj;
                }
            }
            else if (objType === 'Number') {
                value = this.data.selecteds[dateObj];
            }
            result = dateTools.getDateObj(value, 'date');
        }
        if (type === 'year') {
            Reflect.deleteProperty(result, 'DD');
            Reflect.deleteProperty(result, 'MM');
        }
        else if (type === 'month') {
            Reflect.deleteProperty(result, 'DD');
        }
        return result;
    }
    getCurGrids() {
        return this.data.panels.map((k) => k.list).flat(Infinity).filter((k) => !k.exceed);
    }
    findFromPanels(dateObj, grid, grids) {
        let value = this.getDateVal(dateObj, this.data.type), curItems = grids || this.getCurGrids(), curItem = grid || curItems.find((k) => JSON.stringify(k.value) == JSON.stringify(value));
        return { target: curItem, list: curItems };
    }
    renderFinish() {
        if (this.data.type !== 'daytime') {
            let curGrids = this.getCurGrids();
            this.data.panels.forEach((o) => {
                o.list.forEach((k) => {
                    k.contEl.onclick = () => {
                        if (k.disabled)
                            return;
                        if (this.data.selecteds.length > this.options.maxSelection) {
                            console.warn(`The selected items have exceeded the ${this.options.maxSelection} limit!`);
                            return;
                        }
                        let curItem = curGrids.find((i) => JSON.stringify(i.value) == JSON.stringify(k.value));
                        if (this.options.mode === 'multiple') {
                            if (curItem) {
                                curItem.selected ? this.deselect(k, curItem, curGrids) : this.select(k, curItem, curGrids);
                            }
                            else {
                                this.select(k, curItem, curGrids);
                            }
                        }
                        else {
                            this.select(k, curItem, curGrids);
                        }
                        super.listen({ name: 'set', params: [k] });
                    };
                    if (this.detailIns) {
                        k.contEl.onmouseenter = () => {
                            this.showEvent(k);
                            this.detailIns.transfer(k.contEl, () => {
                                this.detailIns.show();
                            });
                        };
                    }
                });
            });
            if (this.resultIns) {
                this.resultIns.on('removed', (items) => {
                    for (let k of items) {
                        this.deselect(k.source);
                    }
                });
                this.resultIns.on('updated', () => {
                    this.resultIns.data.forEach((k) => {
                        k.wrapEl.onclick = (e) => {
                            e.target.getAttribute(alias) !== 'remove' && !this.findFromPanels(k.label).target && this.createPanels(k.label);
                        };
                    });
                });
            }
            if (this.prevYearEl) {
                this.prevYearEl.onclick = () => {
                    let years = (this.data.type === 'year') ? -18 * this.options.cols * this.options.rows :
                        (this.data.type === 'month') ? -this.options.cols * this.options.rows : -1;
                    this.createPanels(dateTools.getOffsetDate(this.data.startDate, { YYYY: years }), true);
                    this.data.year.panelEl && this.createYearList(this.data.panels[this.panelIdx], null, this.panelIdx);
                };
            }
            if (this.nextYearEl) {
                this.nextYearEl.onclick = () => {
                    let years = (this.data.type === 'year') ? 18 * this.options.cols * this.options.rows :
                        (this.data.type === 'month') ? this.options.cols * this.options.rows : 1;
                    this.createPanels(dateTools.getOffsetDate(this.data.startDate, { YYYY: years }), true);
                    this.data.year.panelEl && this.createYearList(this.data.panels[this.panelIdx], null, this.panelIdx);
                };
            }
            if (this.data.type.includes('date')) {
                this.prevMonthEl.onclick = () => {
                    this.createPanels(dateTools.getOffsetDate(this.data.startDate, { MM: -1 }), true);
                };
                this.nextMonthEl.onclick = () => {
                    this.createPanels(dateTools.getOffsetDate(this.data.startDate, { MM: 1 }), true);
                };
            }
            this.data.panels.forEach((k, i) => {
                if (k.yearEl) {
                    k.yearEl.onclick = () => {
                        this.data.year.panelEl.style.top = k.panelEl.offsetTop + k.headEl.clientHeight + 'px';
                        this.data.year.panelEl.style.left = k.panelEl.offsetLeft + 'px';
                        this.data.month.panelEl && elState(this.data.month.panelEl).isVisible && hide({ el: this.data.month.panelEl });
                        this.createYearList(k, null, i);
                        toggle({
                            el: this.data.year.panelEl,
                            done: {
                                show: () => {
                                    k.yearEl.classList.add(`${prefix}opened`);
                                },
                                hide: () => {
                                    k.yearEl.classList.remove(`${prefix}opened`);
                                }
                            }
                        });
                    };
                    this.data.year.prevEl.onclick = () => {
                        this.createYearList(k, this.data.year.list[0].value - 15, i);
                    };
                    this.data.year.nextEl.onclick = () => {
                        this.createYearList(k, this.data.year.list[14].value + 1, i);
                    };
                    this.data.year.btnEl.onclick = () => {
                        this.searchYear2Update(this.data.year.inputEl);
                    };
                    this.data.year.inputEl.onkeyup = (e) => {
                        (e.code === 'Enter') && this.searchYear2Update(this.data.year.inputEl);
                    };
                }
                if (k.monthEl) {
                    k.monthEl.onclick = () => {
                        this.data.month.panelEl.style.top = k.panelEl.offsetTop + k.headEl.clientHeight + 'px';
                        this.data.month.panelEl.style.left = k.panelEl.offsetLeft + 'px';
                        if (elState(this.data.year.panelEl).isVisible) {
                            hide({ el: this.data.year.panelEl });
                        }
                        this.data.month.list.forEach((o) => {
                            if (k.month === o.value) {
                                this.selectedAttrToggle(o, true);
                            }
                            else {
                                this.selectedAttrToggle(o, false);
                            }
                            o.contEl.onclick = () => {
                                if (k.month !== o.value) {
                                    this.data.startDate.setMonth(o.value - i);
                                    this.createPanels('', true);
                                    hide({ el: this.data.month.panelEl });
                                }
                            };
                        });
                        toggle({
                            el: this.data.month.panelEl,
                            done: {
                                show: () => {
                                    k.monthEl.classList.add(`${prefix}opened`);
                                },
                                hide: () => {
                                    k.monthEl.classList.remove(`${prefix}opened`);
                                }
                            }
                        });
                    };
                }
            });
            this.data.selecteds.forEach((k, i) => {
                let item = this.findFromPanels(k, null, curGrids).target;
                if (item) {
                    this.selectedAttrToggle(item, true);
                    (i === 0 && this.options.events.enable) && this.showEvent(item);
                }
            });
            if (this.yearSearchEl) {
                this.yearSearchEl.btnEl.onclick = () => {
                    this.searchYear2Update(this.yearSearchEl.inputEl, false);
                };
                this.yearSearchEl.inputEl.onkeyup = (e) => {
                    (e.code === 'Enter') && this.searchYear2Update(this.yearSearchEl.inputEl, false);
                };
            }
            if (this.timePanelsEl) {
                if (this.options.mode === 'range') {
                    if (this.data.selecteds.length === 0) {
                        this.setDaytime([{ hh: 0, mm: 0, ss: 0 }, { hh: 0, mm: 0, ss: 0 }]);
                    }
                    else if (this.data.selecteds.length === 1) {
                        this.setDaytime([this.data.selecteds[0], { hh: 0, mm: 0, ss: 0 }]);
                    }
                    else {
                        this.setDaytime(this.data.selecteds);
                    }
                }
                else {
                    if (this.data.selecteds.length === 0) {
                        this.setDaytime({ hh: 0, mm: 0, ss: 0 });
                    }
                    else {
                        this.setDaytime(this.data.selecteds[0]);
                    }
                }
                if (this.daytimeBtnEl) {
                    this.daytimeBtnEl.onclick = () => {
                        slideToggle({
                            el: this.timePanelsEl,
                            display: 'grid',
                            before: (e) => {
                                e.for === 'show' && this.openTime2Scroll();
                            }
                        });
                    };
                }
            }
        }
        if (this.options.footer.enable) {
            if (this.confirmEl) {
                this.confirmEl.onclick = () => this.confirm();
            }
            if (this.nowEl) {
                this.nowEl.onclick = () => this.setNow();
            }
            if (this.clearEl) {
                this.clearEl.onclick = () => this.clear(true);
            }
            if (this.cancelEl) {
                this.cancelEl.onclick = () => this.cancel();
            }
            if (this.closeEl) {
                this.closeEl.onclick = () => this.hide();
            }
        }
        if (this.data.type.includes('time') && this.data.daytime.length > 0) {
            let timeCols = this.data.daytime.map((k) => k.colsEl).flat(Infinity);
            if (this.options.display === 'inline') {
                this.openTime2Scroll(timeCols);
            }
            else {
                if (this.bubbleIns) {
                    this.bubbleIns.on('show', () => {
                        this.openTime2Scroll(timeCols);
                    });
                }
            }
        }
        if (this.bubbleIns) {
            this.bubbleIns.on('show', () => {
                this.setResultWidth();
            });
        }
        else {
            this.setResultWidth();
        }
    }
    setResultWidth() {
        this.resultEl && (this.resultEl.style.maxWidth = this.panelsEl.getBoundingClientRect().width + 'px');
    }
    openTime2Scroll(cols) {
        let timeCols = cols || this.data.daytime.map((k) => k.colsEl).flat(Infinity);
        timeCols.forEach((k) => {
            this.showForemost(k);
        });
    }
    searchYear2Update(input, isHide = true) {
        let value = input.value;
        if (value && !isNaN(value)) {
            this.data.startDate.setFullYear(~~value);
            this.createPanels();
            isHide && hide({ el: this.data.year.panelEl });
            this.data.type === 'year' && this.select({ YYYY: ~~value });
        }
        else {
            new Message({
                status: 'error',
                iconShow: true,
                content: this.options.lang.message.requireYearFormat,
            }).show();
        }
    }
    createPanelDayList(elem, dateObj) {
        elem.innerHTML = '';
        let curDate = dateTools.getDate(dateObj, this.data.type, this.options.lang);
        curDate.setDate(1);
        let list = [], startIndex, curMonth = curDate.getMonth(), curYear = curDate.getFullYear(), monthDays = dateTools.getCurDays(curDate), preDate = dateTools.getOffsetDate(curDate, { MM: -1 }), prevMonth = preDate.getMonth(), prevYear = preDate.getFullYear(), nextDate = dateTools.getOffsetDate(curDate, { MM: 1 }), nextMonth = nextDate.getMonth(), nextYear = nextDate.getFullYear(), fragment = document.createDocumentFragment();
        if (this.options.weekStart === 'monday') {
            startIndex = (curDate.getDay() === 0) ? 7 : curDate.getDay();
        }
        else {
            startIndex = (curDate.getDay() === 0) ? 1 : curDate.getDay() + 1;
        }
        let day = 1;
        for (let gridIndex = 1; gridIndex <= 42;) {
            let dayObj = {
                wrapEl: createEl('li'),
                contEl: createEl('span', { [alias]: 'content' }),
                labelEl: createEl('i', { [alias]: 'label' }),
            }, label;
            dayObj.wrapEl.appendChild(dayObj.contEl);
            dayObj.contEl.appendChild(dayObj.labelEl);
            if (gridIndex < startIndex) {
                label = dateTools.getCurDays({ YYYY: prevYear, MM: prevMonth }) - startIndex + gridIndex + 1;
                dayObj.value = { YYYY: prevYear, MM: prevMonth, DD: label };
                dayObj.exceed = true;
                dayObj.wrapEl.setAttribute('exceed', '');
            }
            else if (gridIndex >= startIndex + monthDays) {
                label = gridIndex - startIndex - monthDays + 1;
                dayObj.value = { YYYY: nextYear, MM: nextMonth, DD: label };
                dayObj.exceed = true;
                dayObj.wrapEl.setAttribute('exceed', '');
            }
            else {
                label = day;
                dayObj.value = { YYYY: curYear, MM: curMonth, DD: label };
                day++;
            }
            dayObj.label = label;
            dayObj.labelEl.textContent = label;
            this.setDatespanDisabled(dayObj);
            if (this.options.lunar && this.options.lunar.handler) {
                let lunarObj = this.options.lunar.handler(dayObj.value.YYYY, dayObj.value.MM + 1, dayObj.value.DD);
                if (lunarObj && lunarObj != -1) {
                    let lunarContent = '', attr = '';
                    for (let v of this.options.lunar.arrange) {
                        if (this.options.lunar.map.hasOwnProperty(v) && lunarObj[this.options.lunar.map[v]]) {
                            lunarContent = lunarObj[this.options.lunar.map[v]];
                            if (v.includes('Festival')) {
                                attr = 'festival';
                            }
                            else if (v === 'term') {
                                attr = v;
                            }
                            break;
                        }
                    }
                    dayObj.lunarEl = createEl('u', '', lunarContent);
                    attr ? dayObj.lunarEl.setAttribute([alias], attr) : null;
                    dayObj.labelEl.insertAdjacentElement('afterend', dayObj.lunarEl);
                }
                if (this.options.lunar.tags.length > 0) {
                    for (let v of this.options.lunar.tags) {
                        let date = dateTools.getDateObj(v.date, this.data.type), tagEl = createEl('s', v.attr ? { [alias]: v.attr } : {}, v.content);
                        if (dateTools.isSameDay(date, dayObj.value)) {
                            dayObj.contEl.appendChild(tagEl);
                            break;
                        }
                    }
                }
            }
            if (this.options.events.enable) {
                for (let v of this.options.events.list) {
                    let date = dateTools.getDateObj(v.date, this.data.type);
                    if (dateTools.isSameDay(date, dayObj.value)) {
                        dayObj.busy = true;
                        dayObj.wrapEl.setAttribute('busy', '');
                        break;
                    }
                }
            }
            gridIndex++;
            fragment.appendChild(dayObj.wrapEl);
            list.push(dayObj);
        }
        elem.appendChild(fragment);
        return list;
    }
    createPanelMonthList(elem, dateObj) {
        let list = [];
        [...elem.children].forEach((k, i) => {
            let obj = {
                wrapEl: k,
                contEl: k.querySelector(`[${alias}="content"]`),
                value: { YYYY: dateObj.getFullYear(), MM: i }
            };
            this.setDatespanDisabled(obj);
            list.push(obj);
        });
        return list;
    }
    createPanelYearList(elem, dateObj) {
        let list = [], fragment = document.createDocumentFragment();
        for (let k = 0; k < 18; k++) {
            let year = dateObj.getFullYear() + k, wrap = createEl('li', '', `<span ${alias}="content"><i ${alias}="label">${year}${this.options.lang.unit.Y}</i></span>`), obj = {
                wrapEl: wrap,
                value: { YYYY: year },
            };
            obj.contEl = obj.wrapEl.querySelector(`[${alias}="content"]`);
            this.setDatespanDisabled(obj);
            list.push(obj);
            fragment.appendChild(wrap);
        }
        elem.appendChild(fragment);
        return list;
    }
    createLoopStr(max) {
        let str = '<li>';
        for (let n = 0; n < max; n++) {
            str += `<div> <span ${alias}="content"><i ${alias}="label">${dateTools.fillZero(n)}</i></span></div> `;
        }
        str += '</li>';
        return str;
    }
    createTimeList(label) {
        return `
        <li panel="daytime">
                            <span ${alias}="tools"><i ${alias}="restore" title='${this.options.lang.toolTip.restore}'></i><i ${alias}="reset" title='${this.options.lang.toolTip.reset}'></i><i ${alias}="now" title='${this.options.lang.toolTip.now}'></i><i ${alias}="close" title='${this.options.lang.toolTip.close}'></i></span>
                            <div ${alias}="head">
                                <span ${alias}="time">${label}</span>
                            </div>
                            <div ${alias}="body">
                                <ul ${alias}="column" class="${prefix}reset ${prefix}grid ${prefix}avg-3">
                                    <li>${this.options.lang.unit.h}</li>
                                    <li>${this.options.lang.unit.m}</li>
                                    <li>${this.options.lang.unit.s}</li>
                                </ul>
                                <ul ${alias}="list" class="${prefix}reset ${prefix}grid ${prefix}avg-3">
                                ${this.createLoopStr(24)}
                                ${this.createLoopStr(60)}
                                ${this.createLoopStr(60)}
                                </ul>
                            </div>
                        </li>
        `;
    }
    toggleTimeList(item, list) {
        this.selectedAttrToggle(item, true);
        let selectedItem = list.find((k) => k != item && k.selected);
        selectedItem && this.selectedAttrToggle(selectedItem, false);
    }
    getTimeListArr(parent, type) {
        let children = parent.children, arr = [];
        [...children].forEach((k, i) => {
            let obj = {
                wrapEl: k,
                contEl: k.querySelector(`[${alias}="content"]`),
                value: i
            };
            if (!this.format.includes(type)) {
                obj.disabled = true;
                obj.wrapEl.setAttribute('disabled', '');
            }
            else {
                this.setTimespanDisabled(obj, type);
            }
            arr.push(obj);
        });
        return arr;
    }
    showForemost(parent) {
        let fun = (elem) => {
            let selectedItem = elem.querySelector('[selected]'), toTop = selectedItem.getBoundingClientRect().height * Math.floor((~~selectedItem.textContent) / 2);
            selectedItem && (elem.scrollTop = toTop);
        };
        if (Array.isArray(parent)) {
            parent.forEach((k) => {
                fun(k);
            });
        }
        else {
            fun(parent);
        }
    }
    createTimeSelectPanel() {
        this.timePanelsEl = createEl('UL', { class: `${prefix}reset ${prefix}grid ${prefix}fence ${this.client === 'phone' ? prefix + 'avg-1' : prefix + 'avg-' + this.options.cols}`, panels: 'hms' });
        if (this.options.mode === 'range') {
            this.timePanelsEl.innerHTML = this.createTimeList(this.options.lang.daytime.start) + this.createTimeList(this.options.lang.daytime.end);
        }
        else {
            this.timePanelsEl.innerHTML = this.createTimeList(this.options.lang.daytime.select);
        }
        [...this.timePanelsEl.querySelectorAll(`[panel]`)].forEach((k) => {
            let liArr = [...k.querySelectorAll(`[${alias}="list"] li`)], panelObj = {
                panelEl: k,
                colsEl: liArr,
                restoreEl: k.querySelector(`[${alias}="restore"]`),
                resetEl: k.querySelector(`[${alias}="reset"]`),
                nowEl: k.querySelector(`[${alias}="now"]`),
                closeEl: k.querySelector(`[${alias}="close"]`),
                value: {},
                hh: this.getTimeListArr(liArr[0], 'hh'),
                mm: this.getTimeListArr(liArr[1], 'mm'),
                ss: this.getTimeListArr(liArr[2], 'ss'),
            };
            this.data.daytime.push(panelObj);
        });
        this.footEl.insertAdjacentElement('beforebegin', this.timePanelsEl);
        this.data.daytime.forEach((k, n) => {
            k.closeEl.onclick = () => {
                slideUp({ el: this.timePanelsEl, display: 'inline-grid' });
            };
            k.restoreEl.onclick = () => {
                this.raw[n] && this.setDaytime(this.raw[n], n, true);
            };
            k.resetEl.onclick = () => {
                this.setDaytime({ hh: 0, mm: 0, ss: 0 }, n, true);
            };
            k.nowEl.onclick = () => {
                let time = dateTools.getDateObj(new Date(), this.data.type);
                this.setDaytime({ hh: time.hh, mm: time.mm, ss: time.ss }, n, true);
            };
            k.hh.forEach((i) => {
                i.contEl.onclick = () => {
                    if (i.selected || i.disabled)
                        return;
                    this.toggleTimeList(i, k.hh);
                    k.value.hh = i.value;
                    this.timeToUpdate(k, n, true);
                    super.listen({ name: 'updatedTime', params: [i] });
                };
            });
            k.mm.forEach((i) => {
                i.contEl.onclick = () => {
                    if (i.selected || i.disabled)
                        return;
                    this.toggleTimeList(i, k.mm);
                    k.value.mm = i.value;
                    this.timeToUpdate(k, n, true);
                    super.listen({ name: 'updatedTime', params: [i] });
                };
            });
            k.ss.forEach((i) => {
                i.contEl.onclick = () => {
                    if (i.selected || i.disabled)
                        return;
                    this.toggleTimeList(i, k.ss);
                    k.value.ss = i.value;
                    this.timeToUpdate(k, n, true);
                    super.listen({ name: 'updatedTime', params: [i] });
                };
            });
        });
    }
    timeToUpdate(parent, index = 0, updateHTML = false) {
        updateHTML ? this.setTimeBtnHTML() : null;
        if (this.data.type !== 'daytime' && (this.options.mode === 'single' || this.options.mode === 'range')) {
            (this.data.selecteds.length >= index + 1) && Object.assign(this.selecteds[index], parent.value);
        }
        this.data.type === 'daytime' && this.options.autoFill && this.confirm(false);
    }
    setTimeBtnHTML() {
        if (!this.data.type.includes('time') || !this.daytimeBtnEl)
            return;
        let timeStr = this.data.daytime.map((k) => {
            return isEmpty(k.value) ? this.options.lang.daytime.select : `${dateTools.fillZero(k.value.hh)}:${dateTools.fillZero(k.value.mm)}:${dateTools.fillZero(k.value.ss)}`;
        });
        this.daytimeBtnEl.setAttribute('label', timeStr.join('~'));
    }
    setDaytime(timeObj, index = 0, update = false, cb) {
        if (isEmpty(timeObj) || !this.data.type.includes('time') || this.destroyed)
            return;
        let type = getDataType(timeObj);
        let setFun = (k, i) => {
            let time = (typeof k === 'string') ? dateTools.getTimeObj(timeObj) : k;
            this.data.daytime[i].value.hh = time.hh;
            this.toggleTimeList(this.data.daytime[i].hh[time.hh], this.data.daytime[i].hh);
            this.data.daytime[i].value.mm = time.mm;
            this.toggleTimeList(this.data.daytime[i].mm[time.mm], this.data.daytime[i].mm);
            this.data.daytime[i].value.ss = time.ss;
            this.toggleTimeList(this.data.daytime[i].ss[time.ss], this.data.daytime[i].ss);
            update && this.data.type === 'datetime' ? this.timeToUpdate(this.data.daytime[i], i) : null;
        };
        if (type === 'Array') {
            timeObj.forEach((k, i) => {
                (this.data.daytime.length > i) && setFun(k, i);
            });
        }
        else {
            setFun(timeObj, index);
        }
        this.setTimeBtnHTML();
        super.listen({ name: 'updatedTime', cb, params: [timeObj] });
    }
    assignDaytime() {
        this.selecteds.forEach((k, i) => {
            this.data.daytime[i] && Object.assign(k, this.data.daytime[i].value);
        });
    }
    async cancel(cb) {
        this.options.b4Cancel && await this.options.b4Cancel.call(this);
        this.hide();
        this.clear(true);
        this.handleVal('');
        super.listen({ name: 'canceled', cb });
    }
    hide(cb) {
        this.bubbleIns && this.bubbleIns.hide(() => {
            super.listen({ name: 'hidden', cb });
        });
    }
    show(cb) {
        this.bubbleIns && this.bubbleIns.show(() => {
            super.listen({ name: 'shown', cb });
        });
    }
    async confirm(hide = this.options.confirmHide, cb) {
        if (this.destroyed)
            return;
        this.options.b4Confirm && await this.options.b4Confirm.call(this);
        let result = '';
        if (this.data.type !== 'daytime') {
            if (this.options.mode === 'range') {
                if (this.data.selecteds.length === 0) {
                    result = '';
                }
                else if (this.data.selecteds.length === 2) {
                    let startStr = dateTools.fillFormat(this.data.selecteds[0], this.format, this.options.lang), endStr = dateTools.fillFormat(this.data.selecteds[1], this.format, this.options.lang);
                    result = `${startStr}${this.options.lang.range.hyphen}${endStr} `;
                }
                else {
                    new Message({
                        status: 'error',
                        iconShow: true,
                        content: this.options.lang.message.requireTwoValue,
                    }).show();
                    return false;
                }
            }
            else {
                result = this.data.selecteds.map((k) => dateTools.fillFormat(k, this.format, this.options.lang)).join(this.options.separator);
                if (this.options.required && !result) {
                    new Message({
                        status: 'error',
                        iconShow: true,
                        content: this.options.lang.message.requireOneSelected,
                    }).show();
                    return false;
                }
            }
        }
        else {
            result = this.data.daytime.map((k) => dateTools.fillFormat(k.value, this.format, this.options.lang)).join(this.separator);
        }
        await this.handleVal(result);
        if (hide && this.bubbleIns) {
            this.hide();
            let nextIns = this.options.next && instance.find(this.options.next, 'datetime');
            nextIns?.bubbleIns && nextIns.show();
        }
        super.listen({ name: 'confirmed', cb, params: [result] });
    }
    fillVals(value) {
        if (!this.inputEl || this.options.feature === 'static')
            return;
        let oldVal = this.inputEl.value;
        this.setValFromTgt(value);
        super.listen({ name: 'filled', params: [value] });
        this.output = { value, raw: oldVal, items: this.data.selecteds };
        super.listen({ name: 'output', params: [this.output] });
        super.updateCache({ content: value });
    }
    async handleVal(value) {
        if (this.options.b4Fill) {
            let resp = await this.options.b4Fill.call(this, value, this.separator);
            resp && (value = resp);
        }
        this.value = value;
        !this.options.manual && this.fillVals(value);
    }
    setNow(hide = this.options.nowHide) {
        if (this.destroyed)
            return;
        let date = dateTools.getDateObj(new Date(), this.data.type);
        if (this.data.type === 'daytime') {
            this.setDaytime(date);
        }
        else {
            this.timePanelsEl && this.options.mode !== 'range' && this.setDaytime(date);
            this.select(date);
        }
        if (hide) {
            this.confirm();
            this.hide();
        }
    }
    removeIns() {
        this.bubbleEl.remove();
        this.bubbleIns && (this.bubbleIns.destroy(), this.bubbleIns = null);
        this.detailIns && (this.detailIns.destroy(), this.detailIns = null);
        this.resultIns && (this.resultIns.destroy(), this.resultIns = null);
    }
    async update(settings, cb) {
        if (this.destroyed)
            return;
        this.removeIns();
        if (!isEmpty(settings)) {
            this.updateCache && this.updateCache(settings);
            extend({ target: this.options, source: settings });
            await this.init();
        }
        super.listen({ name: 'updated', cb, params: [this.options] });
        return this;
    }
    setVals2Objs(data) {
        let dataType = getDataType(data), arr = [];
        if (dataType === 'Array') {
            arr = data;
        }
        if (dataType === 'Date') {
            arr = [data];
        }
        else if (dataType === 'Object') {
            arr = [data];
        }
        else if (dataType === 'String') {
            let separator;
            if (data.includes(this.options.separator)) {
                separator = this.options.separator;
            }
            else {
                separator = this.separator;
            }
            arr = data.split(separator);
        }
        if (arr.length === 0) {
            return [];
        }
        else {
            return arr.map((k) => dateTools.getDateObj(k, this.data.type));
        }
    }
    getVals() {
        return { value: this.value, separator: this.separator };
    }
    async setVals(data, cb) {
        if (isEmpty(data) || this.destroyed)
            return;
        let objArr = this.setVals2Objs(data);
        if (objArr.length === 0)
            return;
        if (this.options.b4Set) {
            let resp = await this.options.b4Set.call(this, objArr);
            !isEmpty(resp) && (objArr = resp);
        }
        super.listen({ name: 'set', cb, params: [objArr] });
        if (this.options.mode === 'range') {
            if (objArr.length >= 2) {
                objArr.splice(2, objArr.length - 2);
                this.setRange(objArr);
            }
            else {
                this.select(objArr[0]);
            }
            this.setDaytime(objArr, 0, true);
        }
        else {
            if (this.options.mode === 'single') {
                this.select(objArr[0]);
                this.setDaytime(objArr[0], 0, true);
            }
            else if (this.options.mode === 'multiple') {
                objArr.forEach((k) => {
                    this.select(k);
                });
            }
        }
        super.listen({ name: 'set', cb, params: [objArr] });
        return this;
    }
    setRange(data, cb) {
        if (isEmpty(data) || data.length !== 2 || this.destroyed)
            return;
        let objArr = data.map((k) => dateTools.getDateObj(k, this.data.type)), curGrids = this.getCurGrids();
        dateTools.sort(objArr);
        this.selecteds.splice(0, this.selecteds.length, ...objArr);
        if (!this.inCurPanels(objArr[0], curGrids) && !this.inCurPanels(objArr[1], curGrids)) {
            this.createPanels(objArr[0]);
        }
        else {
            this.setDaytime(objArr);
        }
        this.setRangeAttr();
        super.listen({ name: 'ranged', cb, params: [objArr] });
        return this;
    }
    
    async removeVals(data, cb) {
        if (isEmpty(data) || this.destroyed)
            return;
        let objArr = this.setVals2Objs(data);
        if (objArr.length === 0)
            return;
        this.options.b4Remove && await this.options.b4Remove.call(this, objArr);
        objArr.forEach((k) => {
            this.deselect(k);
        });
        super.listen({ name: 'removed', cb, params: [objArr] });
        return this;
    }
    async clear(resetTime = true, cb) {
        if (this.destroyed)
            return;
        this.options.b4Clear && await this.options.b4Clear.call(this);
        if (this.data.type !== 'daytime') {
            this.selecteds.splice(0);
            this.resultIns && this.resultIns.clear();
            let curGrids = this.getCurGrids();
            curGrids.forEach((k) => {
                if (k.selected) {
                    this.selectedAttrToggle(k, false);
                }
                this.removeRangeAttr(k);
            });
        }
        if (resetTime && this.timePanelsEl) {
            for (let k in this.data.daytime) {
                this.setDaytime({ hh: 0, mm: 0, ss: 0 }, k);
            }
        }
        super.listen({ name: 'cleared', cb, });
        return this;
    }
    async transfer(input, pos, cb) {
        if (this.destroyed)
            return;
        let tmpPos = getEl(pos), tmpInput = getEl(input);
        if (this.options.display === 'popup' && tmpPos) {
            this.positionEl = tmpPos;
            await this.bubbleIns.transfer(pos);
        }
        if (tmpInput) {
            this.inputEl = tmpInput;
            this.inputEl.readOnly = true;
            this.options.autoCorrect && this.checkB4Show();
        }
        super.listen({ name: 'transferred', cb, params: [tmpInput, tmpPos] });
    }
    disable() {
        if (this.destroyed)
            return;
        this.inputEl && this.inputEl.toggleAttribute('inert', true);
        this.bubbleEl.toggleAttribute('inert', true);
        super.listen({ name: 'disabled' });
    }
    enable() {
        if (this.destroyed)
            return;
        this.inputEl && this.inputEl.removeAttribute('inert');
        this.bubbleEl.removeAttribute('inert');
        super.listen({ name: 'enabled' });
    }
    
    destroy(cb) {
        if (this.destroyed)
            return;
        this.removeIns();
        this.confirmEl && (this.confirmEl.onclick = null);
        this.nowEl && (this.nowEl.onclick = null);
        this.clearEl && (this.clearEl.onclick = null);
        this.cancelEl && (this.cancelEl.onclick = null);
        this.closeEl && (this.closeEl.onclick = null);
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

let AXTMP_tipTpl = config.lang.rate.template.tooltip, AXTMP_resultTpl = config.lang.rate.template.result;
const optRate = [
    {
        attr: 'name',
        prop: 'name',
        value: ''
    },
    {
        attr: 'value',
        prop: 'value',
        value: 0,
    },
    {
        attr: 'fill',
        prop: 'fill',
        value: 'value',
    },
    {
        attr: 'heading',
        prop: 'heading',
        value: ''
    },
    {
        attr: 'half',
        prop: 'half',
        value: false,
    },
    {
        attr: 'count',
        prop: 'count',
        value: 5,
    },
    {
        attr: 'increment',
        prop: 'increment',
        value: 1,
    },
    {
        attr: 'tooltip',
        prop: 'tooltip',
        value: {
            enable: false,
            format: AXTMP_tipTpl,
        },
    },
    {
        attr: 'result',
        prop: 'result',
        value: {
            enable: false,
            format: AXTMP_resultTpl,
        },
    },
    {
        attr: 'label',
        prop: 'label',
        value: {
            enable: false,
            format: '',
        },
    },
    {
        attr: 'clearable',
        prop: 'clearable',
        value: false,
    },
    {
        attr: 'size',
        prop: 'size',
        value: 'md',
    },
    {
        attr: 'star',
        prop: 'star',
        value: `${prefix}icon-star-f`,
    },
    {
        attr: 'type',
        prop: 'type',
        value: 'icon',
    },
    {
        attr: 'map',
        prop: 'map',
        value: [],
    },
    {
        attr: 'highlight',
        prop: 'highlight',
        value: false,
    },
    {
        attr: 'omitted',
        prop: 'omitted',
        value: false,
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'disabled',
        prop: 'disabled',
        value: false,
    },
    {
        attr: 'readonly',
        prop: 'readonly',
        value: false,
    },
    {
        attr: 'on-set',
        prop: 'onSet',
        value: null,
    },
    {
        attr: 'on-changed',
        prop: 'onChanged',
        value: null,
    },
    {
        attr: 'on-restored',
        prop: 'onRestored',
        value: null,
    },
    {
        attr: 'on-cleared',
        prop: 'onCleared',
        value: null,
    },
    ...optBase
];

class Rate extends ModBaseListenCache {
    options = {};
    inputEl;
    current;
    evtMap;
    useMap;
    outputIns;
    output;
    rawVal;
    wrapEl;
    tooltipIns;
    tooltipEl;
    clearEl;
    items;
    legends;
    headEl;
    resultEl;
    static hostType = 'node';
    static optMaps = optRate;
    constructor(elem, options = {}, initial = true) {
        super();
        let tmp = getEl(elem), host;
        if (!tmp)
            return this;
        if (tmp.nodeName === 'INPUT') {
            this.inputEl = tmp;
            this.inputEl.type = 'range';
            host = createEl('div', { class: `${prefix}rate` });
            tmp.insertAdjacentElement('beforebegin', host);
        }
        else {
            host = tmp;
            host.classList.add(`${prefix}rate`);
            this.inputEl = createEl('input', { type: 'range' });
        }
        super.ready({
            options,
            maps: Rate.optMaps,
            host,
            component: true,
            spread: ['tooltip', 'result', 'lable']
        });
        this.current = { place: 'full', legend: null, value: 0, stars: 0 };
        this.evtMap = isMobi ? { move: 'ontouchmove', end: 'ontouchend' } : { move: 'onmousemove', end: 'onmouseleave' };
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        this.useMap = !isEmpty(this.options.map) && Array.isArray(this.options.map);
        this.options.count = this.useMap ? this.options.map.length : ~~this.options.count;
        this.createOutputIns();
        this.legends = [];
        this.renderMain();
        this.renderFormat();
        this.setAttrs();
        this.options.value = parseFloat(this.options.value || this.rawHtml || this.inputEl.value);
        this.rawVal = this.options.value;
        this.setVals(this.options.value);
        this.createTooltip();
        this.renderFinish();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    createOutputIns() {
        this.outputIns = new Observe({ stars: 0, value: 0, title: this.options.lang.title.dft, count: this.options.count, total: this.options.count * this.options.increment }, {
            onSet: (data) => {
                if (data.key === 'value') {
                    let oldVal = parseFloat(this.inputEl.value), newVal = this.output[this.options.fill || 'value'];
                    this.inputEl.value = newVal;
                    oldVal !== newVal && super.listen({ name: 'changed', params: [newVal, oldVal] });
                }
            },
            onCompleted: () => {
                this.renderFormat();
            }
        });
        this.output = this.outputIns.proxy;
    }
    renderFinish() {
        if (this.options.readonly || this.options.disabled)
            return;
        this.wrapEl[this.evtMap.move] = (e) => {
            let legend = this.findLegend(e);
            if (!legend)
                return;
            let place = this.getStarPoint(e, legend), { stars, value } = this.updateStarsState({ legend, place });
            if (this.tooltipIns) {
                this.tooltipIns.transfer(legend, () => this.tooltipIns.show());
                this.renderFormat('tooltip', { ...this.output, stars, value });
            }
            this.current = { place, legend, stars, value };
        };
        this.wrapEl.onclick = (e) => {
            let legend = this.findLegend(e);
            if (!legend)
                return;
            this.setVals({ legend, place: this.getStarPoint(e, legend) });
        };
        this.wrapEl[this.evtMap.end] = (e) => {
            this.tooltipIns && this.tooltipIns.hide();
            if (isMobi) {
                this.setVals(this.current);
            }
            else {
                this.updateStarsState(this.output.value);
            }
        };
        this.clearEl.onclick = () => {
            this.clear();
        };
    }
    renderFormat(type, data = { ...this.output }) {
        let el = type === 'result' ? this.resultEl : type === 'tooltip' ? this.tooltipEl : null;
        if (el) {
            this.options[type].enable && (el.innerHTML = renderTpl(this.options[type].format, data));
        }
        else {
            this.options.result.enable && (this.resultEl.innerHTML = renderTpl(this.options.result.format, data));
            this.options.tooltip.enable && (this.tooltipEl.innerHTML = renderTpl(this.options.tooltip.format, data));
        }
    }
    findLegend(e) {
        return this.legends.find((k) => contains(getEvtTarget(e), k));
    }
    getStarPoint(e, node) {
        if (!this.options.half)
            return 'full';
        let tmp = getRectPoints(e, node);
        return tmp.length <= 1 ? '' : tmp.includes('l/3') || tmp.includes('l/2') ? 'half' : 'full';
    }
    createTooltip() {
        if (!this.options.tooltip.enable)
            return;
        this.tooltipIns = new Tooltip(this.wrapEl, extend({
            target: {
                content: this.tooltipEl,
                size: '',
                theme: 'text-lt',
                keepShow: true,
            },
            source: this.options.tooltip,
        }));
    }
    getStarsHtml() {
        let getStar = (val) => this.options.type === 'image' ? `<i><img src="${val}"/></i>` : `<i class="${val}"></i>`, result = '', tpl = '', data, getHtml = (cont) => this.options.label.enable ? `<span ${alias}="label">${cont}</span>` : '';
        if (this.useMap) {
            for (let [i, k] of this.options.map.entries()) {
                data = { id: i + 1, title: k.title, count: this.options.map.length };
                tpl = this.options.label.format ? renderTpl(this.options.label.format, data) : data.title;
                result += `<li ${k.title ? 'title="' + k.title + '"' : ''}>
                <span ${alias}="legend">${getStar(k.on)}${getStar(k.off || k.on)}</span>
                ${getHtml(tpl)}
                </li>`;
            }
        }
        else {
            for (let i = 0; i < this.options.count; i++) {
                data = { id: i + 1, title: '', count: this.options.count };
                tpl = this.options.label.format ? renderTpl(this.options.label.format, data) : data.id + this.options.lang.star;
                result += `<li>
                <span ${alias}="legend">${getStar(this.options.star).repeat(2)}</span>
                ${getHtml(tpl)}
                </li>`;
            }
        }
        return result;
    }
    renderMain() {
        this.targetEl.innerHTML = '';
        let fragment = document.createDocumentFragment();
        fragment.appendChild(this.inputEl);
        this.wrapEl = createEl('ul', { class: `${prefix}reset` }, this.getStarsHtml());
        this.resultEl = createEl('div', { [alias]: 'result' });
        this.tooltipEl = createEl('div', { [alias]: 'tooltip' });
        this.clearEl = createEl('i', { class: `${prefix}icon-close-o-f`, [alias]: 'clear', title: this.options.lang.title.clear });
        this.items = [...this.wrapEl.children];
        this.legends = this.items.map((k) => getEl(`[${alias}="legend"]`, k));
        this.headEl = createEl('i', { [alias]: 'head' }, this.options.heading);
        this.options.heading && fragment.appendChild(this.headEl);
        this.options.clearable && fragment.appendChild(this.clearEl);
        fragment.appendChild(this.wrapEl);
        this.options.result.enable && fragment.appendChild(this.resultEl);
        this.targetEl.appendChild(fragment);
        super.listen({ name: 'rendered', params: [{ targetEl: this.targetEl, headEl: this.headEl, wrapEl: this.wrapEl, resultEl: this.resultEl, clearEl: this.clearEl, items: this.items, legends: this.legends }] });
    }
    setAttrs() {
        this.options.size ? this.targetEl.setAttribute('size', this.options.size) : this.targetEl.removeAttribute('size');
        this.options.classes && classes(this.targetEl).add(this.options.classes);
        this.targetEl.setAttribute('type', this.options.type);
        this.targetEl.toggleAttribute('disabled', this.options.disabled);
        this.targetEl.toggleAttribute('readonly', this.options.readonly);
        this.targetEl.toggleAttribute('omitted', this.options.omitted);
        this.inputEl.step = (this.options.half ? 0.5 : 1) * (this.options.fill === 'value' ? this.options.increment : 1);
        this.inputEl.max = this.options.count * (this.options.fill === 'value' ? this.options.increment : 1);
        this.inputEl.min = 0;
        this.inputEl.disabled = true;
        this.inputEl.name = this.options.name;
        !this.inputEl.hasAttribute('value') && (this.inputEl.value = 0);
    }
    getVals() {
        return { ...this.output };
    }
    correctVal(val) {
        let valType = getDataType(val), value = 0;
        if (valType === 'Object') {
            let idx = this.legends.findIndex((k) => k === val.legend);
            if (idx === -1) {
                value = 0;
            }
            else {
                let tmp = val.place === 'half' ? 0.5 : val.place === 'full' ? 1 : 0;
                value = (idx + tmp) * this.options.increment;
            }
        }
        else {
            let tmp = clampVal({ min: 0, max: this.options.increment * this.options.count, val: parseFloat(val) }), stars = tmp / this.options.increment, integer = Math.floor(stars), float = stars - integer, rest = this.getFloatStar(float);
            value = (integer + rest) * this.options.increment;
        }
        return value;
    }
    getFloatStar(float) {
        if (this.options.half) {
            return !float ? 0 : float > 0 && float <= 0.5 ? 0.5 : 1;
        }
        else {
            return !float ? 0 : 1;
        }
    }
    toggleSelected(li) {
        let other = [...this.wrapEl.children].find((k) => k !== li && k.hasAttribute('selected'));
        other && other.toggleAttribute('selected', false);
        li.toggleAttribute('selected', true);
    }
    updateStarsState(val = 0) {
        let value = this.correctVal(val), stars = value / this.options.increment, b4Stars = Math.floor(stars), curStar = stars - b4Stars, item;
        curStar = this.getFloatStar(curStar);
        for (let i = 0; i < this.legends.length; i++) {
            let el = this.legends[i].firstElementChild, li = el.closest('li');
            if (!value) {
                classes(el).remove(`${prefix}half,${prefix}full`);
            }
            else {
                if (i < b4Stars) {
                    if (this.options.highlight) {
                        
                        if (!curStar && i === b4Stars - 1) {
                            classes(el).replace(`${prefix}half`, `${prefix}full`);
                            this.toggleSelected(li);
                            item = li;
                        }
                        else {
                            classes(el).remove(`${prefix}half,${prefix}full`);
                        }
                    }
                    else {
                        classes(el).replace(`${prefix}half`, `${prefix}full`);
                        if (!curStar && i === b4Stars - 1) {
                            this.toggleSelected(li);
                            item = li;
                        }
                    }
                }
                else if (i === b4Stars) {
                    if (curStar === 0) {
                        classes(el).remove(`${prefix}half,${prefix}full`);
                    }
                    else {
                        if (curStar === 0.5) {
                            classes(el).replace(`${prefix}full`, `${prefix}half`);
                        }
                        else {
                            classes(el).replace(`${prefix}half`, `${prefix}full`);
                        }
                        this.toggleSelected(li);
                        item = li;
                    }
                }
                else {
                    classes(el).remove(`${prefix}half,${prefix}full`);
                }
            }
        }
        return { stars: b4Stars + curStar, value, item };
    }
    setVals(val, cb) {
        let { stars, value, item } = this.updateStarsState(val);
        if (value === this.output.value)
            return;
        this.output.stars = stars;
        this.output.value = value;
        this.output.item = item;
        this.useMap && (this.output.title = this.options.map[Math.ceil(stars) - 1].title);
        super.updateCache({ value });
        super.listen({ name: 'set', cb, params: [{ ...this.output }] });
    }
    restore(cb) {
        if (this.destroyed)
            return this;
        this.setVals(this.rawVal);
        super.listen({ name: 'restored', cb, params: [this.rawVal] });
        return this;
    }
    clear(cb) {
        if (this.destroyed)
            return this;
        this.setVals(0);
        super.listen({ name: 'cleared', cb });
        return this;
    }
    destroy(cb) {
        if (this.destroyed)
            return this;
        this.wrapEl[this.evtMap.move] = null;
        this.wrapEl.onclick = null;
        this.wrapEl[this.evtMap.end] = null;
        this.clearEl.onclick = null;
        this.tooltipIns && this.tooltipIns.destroy();
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

let AXTMP_separator$1 = config.splitHyphen;
const optAccordion = [
    {
        attr: 'name',
        prop: 'name',
        value: '',
    },
    {
        attr: 'content',
        prop: 'content',
        value: '',
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'passive',
        prop: 'passive',
        value: false,
    },
    {
        attr: 'disable',
        prop: 'disable',
        value: '',
    },
    {
        attr: 'readonly',
        prop: 'readonly',
        value: '',
    },
    {
        attr: 'expand',
        prop: 'expand',
        value: {
            value: '',
            all: false,
            only: true,
            linkage: false,
        },
    },
    {
        attr: 'arrow',
        prop: 'arrow',
        value: {
            enable: true,
            show: `${prefix}icon-down`,
            hide: `${prefix}icon-down`,
            anim: `${prefix}rotate180`,
            type: 'icon',
            trigger: 'click',
        },
    },
    {
        attr: 'check',
        prop: 'check',
        value: {
            enable: false,
            type: 'checkbox',
            value: '',
        },
    },
    {
        attr: 'tools',
        prop: 'tools',
        value: {
            enable: false,
            trigger: 'hover',
            children: ['add', 'edit', 'remove'],
        },
    },
    {
        attr: 'output',
        prop: 'output',
        value: {
            enable: true,
            target: '',
            separator: AXTMP_separator$1,
            prop: 'label',
        },
    },
    {
        attr: 'click-line',
        prop: 'clickLine',
        value: 'none',
    },
    {
        attr: 'deferred',
        prop: 'deferred',
        value: false,
    },
    {
        attr: 'delay',
        prop: 'delay',
        value: 100,
    },
    {
        attr: 'drag',
        prop: 'drag',
        value: {
            enable: false,
            exclude: '',
        },
    },
    {
        attr: 'drop',
        prop: 'drop',
        value: {
            attr: 'dropnode',
            global: false,
        },
    },
    {
        attr: 'lamp',
        prop: 'lamp',
        value: {
            enable: false,
            placement: 'bottom',
        },
    },
    {
        attr: 'duration',
        prop: 'duration',
        value: 0,
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'layout',
        prop: 'layout',
        value: 'check(icon|disk|cube|label|tips)custom|tools|badge|arrow',
    },
    {
        attr: 'appear',
        prop: 'appear',
        value: {
            rounded: true,
            bordered: true,
            divided: true,
            gapped: false,
        },
    },
    {
        attr: 'b4-expand',
        prop: 'b4Expand',
        value: null,
    },
    {
        attr: 'b4-collapse',
        prop: 'b4Collapse',
        value: null,
    },
    {
        attr: 'b4-add',
        prop: 'b4Add',
        value: null,
    },
    {
        attr: 'b4-edit',
        prop: 'b4Edit',
        value: null,
    },
    {
        attr: 'b4-remove',
        prop: 'b4Remove',
        value: null,
    },
    {
        attr: 'b4-graft',
        prop: 'b4Graft',
        value: null,
    },
    {
        attr: 'on-rendered',
        prop: 'onRendered',
        value: null,
    },
    {
        attr: 'on-trigger',
        prop: 'onTrigger',
        value: null
    },
    {
        attr: 'on-added',
        prop: 'onAdded',
        value: null
    },
    {
        attr: 'on-edited',
        prop: 'onEdited',
        value: null
    },
    {
        attr: 'on-removed',
        prop: 'onRemoved',
        value: null
    },
    {
        attr: 'on-disabled',
        prop: 'onDisabled',
        value: null
    },
    {
        attr: 'on-enabled',
        prop: 'onEnabled',
        value: null
    },
    {
        attr: 'on-disabledall',
        prop: 'onDisabledAll',
        value: null
    },
    {
        attr: 'on-enabledall',
        prop: 'onEnabledAll',
        value: null
    },
    {
        attr: 'on-readonly',
        prop: 'onReadonly',
        value: null
    },
    {
        attr: 'on-readonlyall',
        prop: 'onReadonlyAll',
        value: null
    },
    {
        attr: 'on-expand',
        prop: 'onExpand',
        value: null
    },
    {
        attr: 'on-expanded',
        prop: 'onExpanded',
        value: null
    },
    {
        attr: 'on-collapse',
        prop: 'onCollapse',
        value: null
    },
    {
        attr: 'on-collapsed',
        prop: 'onCollapsed',
        value: null
    },
    {
        attr: 'on-expandall',
        prop: 'onExpandAll',
        value: null
    },
    {
        attr: 'on-expandedAll',
        prop: 'onExpandedAll',
        value: null
    },
    {
        attr: 'on-collapsedall',
        prop: 'onCollapsedAll',
        value: null
    },
    {
        attr: 'on-selected',
        prop: 'onSelected',
        value: null
    },
    {
        attr: 'on-deselected',
        prop: 'onDeselected',
        value: null
    },
    {
        attr: 'on-selectedall',
        prop: 'onSelectedAll',
        value: null
    },
    {
        attr: 'on-checked',
        prop: 'onChecked',
        value: null
    },
    {
        attr: 'on-unchecked',
        prop: 'onUnchecked',
        value: null
    },
    {
        attr: 'on-checkedall',
        prop: 'onCheckedAll',
        value: null
    },
    {
        attr: 'on-filled',
        prop: 'onFilled',
        value: null
    },
    {
        attr: 'on-turned',
        prop: 'onTurned',
        value: null
    },
    {
        attr: 'on-got',
        prop: 'onGot',
        value: null
    },
    {
        attr: 'on-set',
        prop: 'onSet',
        value: null
    },
    {
        attr: 'on-cleared',
        prop: 'onCleared',
        value: null
    },
    {
        attr: 'on-grafted',
        prop: 'onGrafted',
        value: null
    },
    {
        attr: 'on-updatedcont',
        prop: 'onUpdatedCont',
        value: null
    },
    {
        attr: 'on-request',
        prop: 'onRequest',
        value: null
    },
    {
        attr: 'on-toofew',
        prop: 'onTooFew',
        value: null
    },
    {
        attr: 'on-toomany',
        prop: 'onTooMany',
        value: null
    },
    {
        attr: 'on-locked',
        prop: 'onLocked',
        value: null
    },
    {
        attr: 'on-unlocked',
        prop: 'onUnlocked',
        value: null
    },
    ...optBase
];

class Accordion extends ModBaseListenCacheNest {
    expandEvt;
    lineEvt;
    treeDataOrig;
    receiver;
    rawData;
    observeIns;
    chainChecking;
    hoverIns;
    inputEl;
    value;
    contXhr;
    static hostType = 'node';
    static optMaps = optAccordion;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            host: elem,
            maps: Accordion.optMaps,
            component: true,
            spread: ['output', 'arrow', 'check', 'lamp', 'tools', 'drag']
        });
        
        
        
        
        
        
        
        
        
        
        
        
        
        let _this = this;
        this.expandEvt = debounce(function () {
            let attr = this.getAttribute(alias) === 'arrow' ? 'arrowEl' : 'headEl', item = findItem(this, _this.flatData, attr);
            _this.toggleExpanded(item);
        }, this.options.delay);
        this.lineEvt = debounce(function (e) {
            let item = findItem(this, _this.flatData, 'headEl');
            if (!item)
                return;
            if ((['group', 'gap', 'holder'].includes(e.target.getAttribute(alias)) || e.target === item.headEl)) {
                if (_this.options.clickLine === 'expanded') {
                    _this.toggleExpanded(item);
                }
                else if (_this.options.clickLine === 'checked') {
                    _this.toggleCheck(item);
                }
            }
        }, this.options.delay);
        this.treeDataOrig = [];
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch (err) {
            err ? console.error(err) : console.warn(config.warn.init);
            return this;
        }
        this.receiver = getEl(this.options.output.target);
        super.useTpl();
        await this.getDataToRender();
        this.setAttrs();
        this.renderFinish();
        super.initCheckeds();
        this.initExpandeds();
        super.initDisableds();
        super.initReadonlys();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    async getDataToRender() {
        let base = {
            nodeType: 'accordion',
        }, promise;
        if (this.options.content) {
            promise = treeTools.allToTree({
                content: this.options.content,
                contType: this.options.contType,
                contData: this.options.contData,
                ajax: { spinSel: this.targetEl, xhrName: 'contXhr' },
                ins: this,
                ...base
            });
        }
        else {
            promise = treeTools.allToTree({
                content: this.targetEl.innerHTML,
                ...base
            });
        }
        await promise.then((res) => {
            
            this.treeDataOrig = res.data && Array.isArray(res.data) ? res.data : res;
            this.rawData = res;
        });
        this.targetEl.innerHTML = '';
        this.renderData(this.treeDataOrig);
        this.getTreeFlat();
        super.listen({ name: 'rendered', params: [this.treeData, this.flatData] });
        return this;
    }
    getTreeFlat() {
        this.treeData = this.getObserver(this.treeDataOrig).proxy;
        this.flatData = this.treeData;
    }
    getObserver(data) {
        this.observeIns = new Observe(data, {
            deep: {
                enable: true,
                exclude: ['tools'],
            },
            onSet: (obj) => {
                if (obj.key === 'icon' && obj.proxy.iconEl) {
                    classes(obj.proxy.iconEl).replace(obj.raw, obj.value);
                }
                else if (obj.key === 'cube' && obj.proxy.cubeEl) {
                    obj.proxy.cubeEl.src = obj.value;
                }
                else if (obj.key === 'disk' && obj.proxy.diskEl) {
                    obj.proxy.diskEl.src = obj.value;
                }
                else if (obj.key === 'label') {
                    obj.proxy.labelEl.innerHTML = obj.value;
                }
                else if (obj.key === 'tips' && obj.proxy.tipsEl) {
                    obj.proxy.tipsEl.innerHTML = obj.value;
                }
                else if (obj.key === 'content') {
                    super.updateElCont(obj.proxy, obj.value, 'content');
                }
                else if (obj.key === 'extra') {
                    super.updateElCont(obj.proxy, obj.value, 'extra');
                }
                else if (obj.key === 'custom' && obj.proxy.customEl) {
                    super.updateElCont(obj.proxy, obj.value, 'custom');
                }
                else if (obj.key === 'badge' && obj.proxy.badgeEl) {
                    obj.proxy.badgeEl.setAttribute('label', obj.value);
                }
                else if (obj.key === 'href' && obj.proxy.labelEl.nodeName === 'A') {
                    obj.proxy.labelEl.href = obj.value;
                }
                else if (obj.key === 'rel' && obj.proxy.labelEl.nodeName === 'A') {
                    obj.proxy.labelEl.rel = obj.value;
                }
                else if (obj.key === 'target' && obj.proxy.labelEl.nodeName === 'A') {
                    obj.proxy.labelEl.target = obj.value;
                }
                else if (obj.key === 'expanded') {
                    super.toggleArrow(obj.value, obj.proxy);
                }
                else if (obj.key === 'disabled') {
                    obj.proxy.wrapEl.toggleAttribute('disabled', obj.value);
                }
                else if (obj.key === 'readonly') {
                    obj.proxy.headEl.toggleAttribute('readonly', obj.value);
                }
                else if (obj.key === 'checked') {
                    obj.proxy.headEl.toggleAttribute('checked', obj.value);
                    obj.proxy.checkEl.setAttribute('check', obj.value ? 'ed' : '');
                }
                else if (obj.key === 'children') {
                    if (!obj.raw && obj.value) ;
                    else if (obj.raw && !obj.value) ;
                }
            },
            onDeleted: (obj) => {
                if (obj.key === 'children') {
                    treeTools.parentToChild(obj.proxy);
                }
            },
            onCompleted: (data) => {
                data.keys.set.includes('checked') && this.updateVals();
                if (this.options.storName) {
                    let tmp = {
                        check: { value: treeTools.getBoolItems(this.flatData, 'checked').map((k) => k.id) },
                        expand: treeTools.getBoolItems(this.flatData, 'expanded').map((k) => k.id),
                        disable: treeTools.getBoolItems(this.flatData, 'disabled').map((k) => k.id),
                        readonly: treeTools.getBoolItems(this.flatData, 'readonly').map((k) => k.id),
                        content: !isEmpty(this.options.content) ? deepClone(this.treeDataOrig) : '',
                    };
                    super.updateCache(tmp);
                }
            }
        });
        return this.observeIns;
    }
    initExpandeds() {
        let vals = valToArr(this.options.expand.value).map((k) => findItem(k, this.flatData)).filter(Boolean), items = this.flatData.filter((k) => k.expanded), tmp = [...items, ...vals];
        for (let k of items)
            k.expanded = false;
        if (this.options.expand.all) {
            this.expandAll();
        }
        else {
            super.expand(tmp);
        }
    }
    renderData(data) {
        for (let k of data) {
            this.createHeadEl(k);
            this.createBodyEl(k);
            k.wrapEl.append(k.headEl, k.bodyEl);
            this.targetEl.appendChild(k.wrapEl);
        }
        this.inputEl = createEl('input', { type: 'hidden', name: this.options.name });
        this.options.name && this.targetEl.insertAdjacentElement('afterbegin', this.inputEl);
    }
    updateArrowEl(item) {
        if (!item.arrowEl)
            return;
        item.arrowEl.setAttribute('type', this.options.arrow.type);
        this.options.lang.title.arrow && item.arrowEl.setAttribute('title', this.options.lang.title.arrow);
        if (this.options.arrow.type === 'image') {
            this.options.arrow.hide && (item.arrowEl.style.backgroundImage = `url("${this.options.arrow.hide}")`);
        }
        else {
            item.arrowEl.classList.remove(`${prefix}none`);
            this.options.arrow.hide && item.arrowEl.classList.add(this.options.arrow.hide);
        }
    }
    
    createHeadEl(item) {
        if (item.wrapEl)
            return;
        item.wrapEl = createEl('section', { class: `${prefix}accordion-wrap` });
        if (item.headEl) {
            if (!item.labelEl) {
                item.labelEl = createEl(item.hasOwnProperty('href') ? 'a' : 'i', { [alias]: 'label' }, item.label);
                item.headEl.appendChild(item.labelEl);
            }
        }
        else {
            item.headEl = createEl('div', { class: `${prefix}accordion-head` });
            !item.labelEl && (item.labelEl = createEl(item.hasOwnProperty('href') ? 'a' : 'i', { [alias]: 'label' }, item.label));
            item.headEl.appendChild(item.labelEl);
        }
        item.wrapEl.appendChild(item.headEl);
        item.href && (item.labelEl.href = item.href);
        item.target && (item.labelEl.target = item.target);
        item.groupEl = createEl('div', { [alias]: 'group' });
        super.getArrowEl(item);
        let imgNone = getImgNone();
        if (!item.iconEl) {
            item.iconEl = item.hasOwnProperty('icon') ? createEl('i', { [alias]: 'icon', class: item.icon }) : null;
        }
        if (!item.diskEl) {
            item.diskEl = item.hasOwnProperty('disk') ? createEl('img', { [alias]: 'disk', src: item.disk || imgNone }) : null;
        }
        if (!item.cubeEl) {
            item.cubeEl = item.hasOwnProperty('cube') ? createEl('img', { [alias]: 'cube', src: item.cube || imgNone }) : null;
        }
        if (!item.imageEl) {
            item.imageEl = item.hasOwnProperty('image') ? createEl('img', { [alias]: 'image', src: item.image || imgNone }) : null;
        }
        if (!item.badgeEl) {
            item.badgeEl = item.badge ? createEl('ax-badge', { [alias]: 'badge', label: item.badge.toString().trim() }) : null;
        }
        if (!item.tipsEl) {
            item.tipsEl = item.tips ? createEl('i', { [alias]: 'tips' }, item.tips) : null;
        }
        if (!item.customEl) {
            item.customEl = item.custom ? createEl('div', { [alias]: 'custom' }, item.custom) : null;
        }
        item.expanded && item.headEl.toggleAttribute('expanded', true);
        item.disabled && item.headEl.toggleAttribute('disabled', true);
        item.readonly && item.headEl.toggleAttribute('readonly', true);
        item.checked && item.headEl.toggleAttribute('checked', true);
        if (this.options.check.enable) {
            item.checkEl = createEl(`ax-${this.options.check.type}`, { [alias]: 'check' });
        }
        if (this.options.tools.enable) {
            item.toolsEl = createTools(this.options.tools.children, item.headEl);
            item.toolsEl.setAttribute(alias, 'tools');
            item.tools = deepClone(this.options.tools.children);
            for (let k of item.tools) {
                this.options.lang.title[k.name] && k.wrapEl.setAttribute('title', this.options.lang.title[k.name]);
                if (k.name === 'add') {
                    item.addEl = k.wrapEl;
                }
                else if (k.name === 'remove') {
                    item.removeEl = k.wrapEl;
                }
                else if (k.name === 'edit') {
                    item.editEl = k.wrapEl;
                }
            }
        }
        super.parseLayout(item.headEl, this.options.layout, {
            arrow: item.arrowEl,
            label: item.labelEl,
            check: item.checkEl,
            icon: item.iconEl,
            disk: item.diskEl,
            cube: item.cubeEl,
            badge: item.badgeEl,
            tips: item.tipsEl,
            custom: item.customEl,
            tools: item.toolsEl,
            group: item.groupEl
        });
    }
    createBodyEl(item) {
        item.bodyEl = createEl('div', { class: `${prefix}accordion-body` });
        !item.contEl && (item.contEl = createEl('div', { class: `${prefix}accordion-cont` }));
        item.bodyEl.appendChild(item.contEl);
        if (!item.extraEl && item.hasOwnProperty('extra')) {
            item.extraEl = createEl('div', { class: `${prefix}accordion-extra` });
        }
        item.extraEl && item.bodyEl.appendChild(item.extraEl);
        !this.options.deferred && this.setContExtra(item, true);
        item.headEl.insertAdjacentElement('afterend', item.bodyEl);
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}accordion`);
        this.options.classes && classes(this.targetEl).add(this.options.classes);
        this.targetEl.toggleAttribute('inert', this.options.passive);
        this.targetEl.setAttribute('tools-trigger', this.options.tools.trigger);
        this.options.lamp.enable ? this.targetEl.setAttribute('lamp', this.options.lamp.placement) : this.targetEl.removeAttribute('lamp');
        for (let k in this.options.appear) {
            let val = this.options.appear[k];
            this.targetEl.toggleAttribute(k, val);
        }
    }
    updateExpandEvt(k) {
        if (!this.options.arrow.enable)
            return;
        if (this.options.arrow.trigger === 'click') {
            k.arrowEl.removeEventListener('click', this.expandEvt);
            k.arrowEl.addEventListener('click', this.expandEvt, false);
        }
        else if (this.options.arrow.trigger === 'hover') {
            k.hoverIns && k.hoverIns.destroy();
            k.hoverIns = new Hover(k.arrowEl, {
                onMove: (isIn) => {
                    !isIn && this.options.expand.only ? super.collapse(k) : null;
                },
                onEnter: () => {
                    super.expand(k);
                },
                onLeave: () => {
                    super.collapse(k);
                }
            });
        }
    }
    updateEvt(k) {
        this.updateExpandEvt(k);
        if (k.toolsEl) {
            if (k.removeEl) {
                k.removeEl.onclick = () => {
                    if (k.readonly)
                        return;
                    this.remove(k);
                };
            }
            if (k.editEl) {
                k.editEl.onclick = () => {
                    if (k.readonly)
                        return;
                    this.inputLabelCont(k);
                };
            }
            if (k.addEl) {
                k.addEl.onclick = () => {
                    if (k.readonly)
                        return;
                    this.add({ target: k });
                };
            }
            for (let [i, o] of this.options.tools.children.entries()) {
                let tool = k.tools[i], refer = { ins: this, item: k };
                o.action && o.action.call(refer, tool);
                tool.action && tool.action.call(refer, tool);
            }
        }
        if (['expanded', 'checked'].includes(this.options.clickLine)) {
            k.headEl.removeEventListener('click', this.lineEvt);
            k.headEl.addEventListener('click', this.lineEvt, false);
        }
        k.disabled && k.headEl.toggleAttribute('disabled', true);
        k.readonly && k.headEl.toggleAttribute('readonly', true);
        if (this.options.check.enable) {
            k.checkEl.on('check', (val) => {
                !this.chainChecking && val.checked !== k.checked && this.check(k, val.checked);
            });
        }
        if (this.options.drag.enable) ;
        k.action && k.action.call(this, k);
    }
    renderFinish() {
        for (let k of this.flatData) {
            this.updateEvt(k);
        }
    }
    getTriggerEl(item) {
        return (item.href && item.arrowEl ? item.arrowEl : item.headEl);
    }
    addTrigger(item, target) {
        if (item.disabled)
            return;
        let triggerEl = target || this.getTriggerEl(item);
        if (this.options.arrow.trigger === 'click') {
            triggerEl.removeEventListener('click', this.expandEvt);
            triggerEl.addEventListener('click', this.expandEvt, false);
        }
        else if (this.options.arrow.trigger === 'hover') {
            this.hoverIns ? this.hoverIns.destroy() : null;
            this.hoverIns = new Hover(item.wrapEl, {
                onEnter: () => {
                    super.expand(item);
                    super.listen({ name: 'trigger', params: [item] });
                },
                onLeave: () => {
                    super.collapse(item);
                }
            });
        }
    }
    removeTrigger(item, target) {
        if (item.disabled)
            return;
        let triggerEl = target || this.getTriggerEl(item);
        if (this.options.arrow.trigger === 'click') {
            triggerEl.removeEventListener('click', this.expandEvt);
        }
        else if (this.options.arrow.trigger === 'hover') {
            this.hoverIns ? this.hoverIns.destroy() : null;
        }
    }
    toggleExpanded(data) {
        if (this.destroyed)
            return;
        let item = findItem(data, this.flatData);
        if (!item)
            return;
        item.expanded ? super.collapse(item) : super.expand(item);
        super.listen({ name: 'trigger', params: [item] });
    }
    async setContExtra(item, force = false) {
        await super.getElCont({
            target: item,
            data: item.content,
            prop: 'content',
            cb: (cont) => {
                if (cont !== item.content) {
                    item.content = cont;
                    force && super.updateElCont(item, cont, 'content');
                }
                else {
                    super.updateElCont(item, cont, 'content');
                }
            }
        });
        if (item.extraEl) {
            await super.getElCont({
                target: item,
                data: item.extra,
                prop: 'extra',
                cb: (cont) => {
                    if (cont !== item.extra) {
                        item.extra = cont;
                        force && super.updateElCont(item, cont, 'extra');
                    }
                    else {
                        super.updateElCont(item, cont, 'extra');
                    }
                }
            });
        }
    }
    async eachCollapse(data, cb) {
        if (isNull(data))
            return;
        try {
            if (this.options.b4Collapse) {
                let resp = await this.options.b4Collapse.call(this, data);
                resp && (data = resp);
            }
        }
        catch (err) {
            err ? console.error(err) : console.warn('Branch collapsing has been prevented!');
            return;
        }
        let item = findItem(data, this.flatData);
        if (!item)
            return;
        super.listen({ name: 'collapse', cb, params: [item] });
        item.expanded = false;
        slideUp({
            el: item.bodyEl,
            duration: this.options.duration,
            done: () => {
                super.listen({ name: 'collapsed', cb, params: [item] });
            }
        });
    }
    async eachExpand(data, cb) {
        if (isNull(data))
            return;
        try {
            if (this.options.b4Expand) {
                let resp = await this.options.b4Expand.call(this, data);
                resp && (data = resp);
            }
        }
        catch (err) {
            err ? console.error(err) : console.warn('Branch expansion has been prevented!');
            return;
        }
        let item = findItem(data, this.flatData);
        if (!item)
            return;
        super.listen({ name: 'expand', cb, params: [item] });
        if (this.options.deferred && !item.contEl.innerHTML) {
            item.arrowEl && item.arrowEl.toggleAttribute('spinning', true);
            await this.setContExtra(item);
            item.arrowEl && item.arrowEl.removeAttribute('spinning');
        }
        item.expanded = true;
        slideDown({
            el: item.bodyEl,
            duration: this.options.duration,
            done: () => {
                super.listen({ name: 'expanded', cb, params: [item] });
            }
        });
        if (this.options.expand.only) {
            let others = this.flatData.filter((k) => k !== item && k.expanded);
            for (let k of others)
                super.collapse(k);
        }
    }
    uncheckSibings(item) {
        this.chainChecking = true;
        let siblings = super.getSiblings(item).filter((k) => k.checked);
        for (let k of siblings)
            k.checked = false;
        this.chainChecking = false;
    }
    check(data, flag = true, cb) {
        if (this.destroyed)
            return;
        let tmp = Array.isArray(data) ? data : [data], filters = tmp.map((k) => findItem(k, this.flatData)), items = filters.filter((k) => k && !k.checked === flag), fn = (obj) => {
            obj.checked = flag;
            flag && this.options.check.type === 'radio' && this.uncheckSibings(obj);
        };
        if (!items.length)
            return;
        for (let k of items)
            fn(k);
        super.listen({ name: flag ? 'checked' : 'unchecked', cb, params: [items] });
        return this;
    }
    checkAll(cb) {
        if (this.destroyed || !this.options.check.enable || this.options.check.type !== 'checkbox')
            return;
        if (this.options.check.type === 'checkbox') {
            this.check(this.flatData, true);
        }
        else {
            let checkedItem = this.flatData.find((k) => k.checked);
            !checkedItem && this.check(this.flatData[0], true);
        }
        super.listen({ name: 'checkedAll', cb });
        return this;
    }
    async add(options) {
        if (this.destroyed)
            return;
        let opts = Object.assign({ repeat: true, autoFill: true, isFront: false }, options), type = getDataType(opts.data), target = findItem(opts.target, this.flatData), tmp = [], items = [], appendFun = (data) => {
            let obj = treeTools.createBranchObj({
                source: data,
                flatData: this.flatData,
                target,
                isLeaf: true,
                isChild: false
            });
            this.createHeadEl(obj);
            this.createBodyEl(obj);
            treeTools.addBranch({
                source: obj,
                rootEl: this.targetEl,
                flatData: this.flatData,
                treeData: this.treeData,
                brother: target,
                isFront: opts.isFront,
                repeat: opts.repeat,
                autoFill: opts.autoFill,
                cb: (item) => {
                    items.push(item);
                    this.updateEvt(item);
                }
            });
        };
        tmp = isNull(opts.data) ? [null] : ((type === 'Array' && opts.data.length > 0) ? opts.data : [opts.data]);
        for (let k of tmp) {
            try {
                if (this.options.b4Add) {
                    let resp = await this.options.b4Add.call(this, k);
                    resp && (k = resp);
                }
            }
            catch (err) {
                err ? console.error(err) : console.warn(`Adding new branche (${JSON.stringify(k)}) has been prevented!`);
                continue;
            }
            appendFun(k);
        }
        super.listen({ name: 'added', cb: opts.cb, params: [items] });
        return this;
    }
    async remove(data, cb) {
        if (this.destroyed || isNull(data))
            return;
        if (this.flatData.length === 0) {
            console.warn('The source data is already empty!');
            return;
        }
        let tmp = [], items = [], removeItem = (child) => {
            treeTools.removeBranch({
                source: child,
                flatData: this.flatData,
                treeData: this.treeData,
                remove: false,
                cb: (obj) => {
                    items.push(obj);
                }
            });
        };
        tmp = Array.isArray(data) ? data : [data];
        for (let k of tmp) {
            try {
                if (this.options.b4Remove) {
                    let resp = await this.options.b4Remove.call(this, k);
                    resp && (k = resp);
                }
            }
            catch (err) {
                err ? console.error(err) : console.warn(`Removing old branch (${JSON.stringify(k)}) has been prevented!`);
                continue;
            }
            removeItem(k);
        }
        super.listen({ name: 'removed', cb, params: [items] });
        return this;
    }
    inputLabelCont(item) {
    }
    async edit(item, data, cb) {
        if (this.destroyed || isNull(item) || isEmpty(data)) {
            return this;
        }
        let source = findItem(item, this.flatData);
        if (!source)
            return this;
        for (let k in data) {
            let val = data[k];
            try {
                if (this.options.b4Edit) {
                    let resp = await this.options.b4Edit.call(this, { key: k, value: val }, source);
                    resp && (val = resp);
                }
                if (k === 'action') {
                    source[k] = val.call({ ins: this, item: k }, source);
                }
                else if (['icon', 'disk', 'cube', 'href', 'target', 'rel', 'label', 'content', 'tips', 'badge'].includes(k)) {
                    source[k] = val;
                }
            }
            catch (err) {
                err ? console.error(err) : console.warn(`No changes have been made to the ${k} property!`);
                return this;
            }
        }
        super.listen({ name: 'edited', cb, params: [source] });
        return this;
    }
    async graft({ source, target, isFront = true, isChild = true, cb }) {
        if (this.destroyed || isNull(source))
            return;
        try {
            if (this.options.b4Graft) {
                let resp = await this.options.b4Graft.call(this, source, target);
                resp && resp.source && (source = resp.source);
                resp && resp.target && (source = resp.target);
            }
        }
        catch (err) {
            err ? console.error(err) : console.warn(`The branch relationship between source and target in the tree remains unchanged!`);
            return this;
        }
        let sourceObj = findItem(source, this.flatData, '', { node: 'wrapEl' }), targetObj = findItem(target, this.flatData);
        if (!sourceObj)
            return;
        treeTools.graftBranch({
            source: sourceObj,
            target: targetObj,
            isFront,
            isChild,
            flatData: this.flatData,
            treeData: this.treeData,
            rootEl: this.targetEl,
            cb: (obj) => {
                super.listen({ name: 'grafted', cb, params: [obj] });
                return this;
            }
        });
        return this;
    }
    updateVals() {
        if (!this.options.output.enable)
            return;
        let val = this.getVals();
        this.options.name && (this.inputEl.value = val);
        fieldTools.setVals({ target: this.receiver, value: val });
    }
    setVals(target = this.receiver, cb) {
        if (this.destroyed)
            return;
        let receiver = getEl(target), val = this.getVals();
        fieldTools.setVals({ target: receiver, value: val });
        super.listen({ name: 'set', cb, params: [val] });
        return this;
    }
    clearVals(cb) {
        if (this.destroyed)
            return this;
        super.uncheckAll();
        this.value = '';
        fieldTools.setVals({ target: this.receiver, value: '' });
        this.options.name && (this.inputEl.value = '');
        super.listen({ name: 'cleared', cb });
        return this;
    }
    getVals(opt = {}, cb) {
        if (this.destroyed)
            return this;
        let options = Object.assign({ ...this.options.output, isStr: true }, opt), items = super.getCheckeds(), result = items.map((k) => k[options.prop]);
        let val = options.isStr ? result.join(options.separator) : result;
        super.listen({ name: 'got', cb, params: [val] });
        return val;
    }
    async setContent(item, data = {}, cb) {
        if (this.destroyed)
            return;
        await super.setElCont({
            item,
            data,
            prop: 'content',
            cb: (cont, target) => {
                super.listen({ name: 'filled', cb, params: [cont, target] });
            }
        });
        return this;
    }
    
    async updateCont(content, cb) {
        if (this.destroyed)
            return this;
        await getContent.call(this, {
            content,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: {
                xhrName: 'contXhr',
                ...this.options.ajax
            },
            request: (data) => {
                this.listen({ name: 'request', params: [data] });
            },
            cb: (data) => {
                if (Array.isArray(data)) {
                    this.treeDataOrig = deepClone(data);
                    treeTools.addIdPath({ source: this.treeDataOrig });
                    this.targetEl.innerHTML = '';
                    this.renderData(this.treeDataOrig);
                    this.getTreeFlat();
                    this.renderFinish();
                    super.updateCache({ content });
                    super.listen({ name: 'updatedCont', cb, params: [this.treeDataOrig] });
                }
            }
        });
        return this;
    }
    
    destroy(cb) {
        if (this.destroyed)
            return this;
        for (let k of this.flatData) {
            if (this.options.arrow.trigger === 'click') {
                let triggerEl = k.hasOwnProperty('href') && k.arrowEl ? k.arrowEl : k.headEl;
                triggerEl.removeEventListener('click', this.expandEvt);
            }
            else if (this.options.arrow.trigger === 'hover') {
                this.hoverIns.destroy();
            }
            ['expanded', 'checked'].includes(this.options.clickLine) && k.headEl.removeEventListener('click', this.lineEvt);
            this.options.drag.enable && k.dragIns.destroy();
        }
        this.contXhr && this.contXhr.abort();
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

const optEditor = [
    {
        attr: 'name',
        prop: 'name',
        value: '',
    },
    {
        attr: 'value',
        prop: 'value',
        value: '',
    },
    {
        attr: 'content',
        prop: 'content',
        value: '',
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'disabled',
        prop: 'disabled',
        value: false,
    },
    {
        attr: 'readonly',
        prop: 'readonly',
        value: false,
    },
    {
        attr: 'deferred',
        prop: 'deferred',
        value: false,
    },
    {
        attr: 'delay',
        prop: 'delay',
        value: 200,
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'appear',
        prop: 'appear',
        value: {
            rounded: true,
            bordered: true,
        },
    },
    {
        attr: 'mode',
        prop: 'mode',
        value: 'editor',
    },
    {
        attr: 'header',
        prop: 'header',
        value: {
            enable: true,
            children: [],
        },
    },
    {
        attr: 'footer',
        prop: 'footer',
        value: {
            enable: true,
        },
    },
    {
        attr: 'min-height',
        prop: 'minHeight',
        value: '',
    },
    {
        attr: 'max-height',
        prop: 'maxHeight',
        value: '',
    },
    {
        attr: 'feature',
        prop: 'feature',
        value: 'simple',
    },
    {
        attr: 'b4-paste',
        prop: 'b4Paste',
        value: null,
    },
    {
        attr: 'on-edited',
        prop: 'onEdited',
        value: null
    },
    {
        attr: 'on-disabled',
        prop: 'onDisabled',
        value: null
    },
    {
        attr: 'on-enabled',
        prop: 'onEnabled',
        value: null
    },
    {
        attr: 'on-readonly',
        prop: 'onReadonly',
        value: null
    },
    {
        attr: 'on-filled',
        prop: 'onFilled',
        value: null
    },
    {
        attr: 'on-set',
        prop: 'onSet',
        value: null
    },
    {
        attr: 'on-cleared',
        prop: 'onCleared',
        value: null
    },
    {
        attr: 'on-updatedcont',
        prop: 'onUpdatedCont',
        value: null
    },
    {
        attr: 'on-request',
        prop: 'onRequest',
        value: null
    },
    {
        attr: 'on-output',
        prop: 'onOutput',
        value: null
    },
    ...optBase
];

class Editor extends ModBaseListenCache {
    options = {};
    flatData;
    treeData;
    inputEl;
    headEl;
    bodyEl;
    contEl;
    footEl;
    pathEl;
    stateEl;
    maskEl;
    toolsEl;
    output;
    tmpRaw;
    contXhr;
    keyupEvt;
    pointerEvt;
    inputEvt;
    pasteEvt;
    readonlyEvt;
    tools;
    static hostType = 'node';
    static optMaps = optEditor;
    constructor(elem, options = {}, initial = true) {
        super();
        let tmp = getEl(elem), host;
        if (!tmp)
            return this;
        if (['TEXTAREA'].includes(tmp.nodeName)) {
            this.inputEl = tmp;
            this.inputEl.classList.add(`${prefix}editor-source`);
            host = createEl('div', { class: `${prefix}editor` });
            tmp.insertAdjacentElement('afterend', host);
        }
        else {
            host = tmp;
            host.classList.add(`${prefix}editor`);
            this.inputEl = createEl('textarea', { class: `${prefix}editor-source` });
        }
        super.ready({
            options,
            host,
            maps: Editor.optMaps,
            component: true,
            spread: ['header', 'footer']
        });
        
        
        
        
        
        
        
        
        
        
        
        
        
        this.rawHtml = this.inputEl.value || this.targetEl.innerHTML;
        this.contEl = createEl('div', { class: `${prefix}editor-cont`, placeholder: this.options.lang.placeholder, contenteditable: true });
        let _this = this;
        this.keyupEvt = debounce((e) => {
            let code = e.key;
            if (code === 'Backspace') {
                
                ([`<br>`, `<p><br></p>`, `<div><br></div>`].includes(trim(this.contEl.innerHTML, 'global'))) && (this.contEl.innerHTML = '');
                this.updateInputVal();
            }
        }, this.options.delay);
        this.pointerEvt = () => {
            this.setPathInfo();
        };
        this.inputEvt = debounce(function (e) {
            if (this === _this.contEl) {
                _this.inputEl.value = _this.contEl.innerHTML;
            }
            else if (this === _this.inputEl) {
                _this.contEl.innerHTML = _this.inputEl.value;
            }
            _this.output.value = _this.inputEl.value;
            _this.updateInfo();
        }, {
            delay: this.options.delay,
            
        });
        this.pasteEvt = async function (e) {
            let text = '';
            if (_this.options.b4Paste) {
                e.preventDefault();
                text = await _this.options.b4Paste.call(_this, await navigator.clipboard.readText());
                if (text) {
                    let selection = window.getSelection();
                    if (selection.rangeCount > 0) {
                        let range = selection.getRangeAt(0), textNode = document.createTextNode(text);
                        range.insertNode(textNode);
                        range.setStartAfter(textNode);
                        range.collapse(true);
                        selection.removeAllRanges();
                        selection.addRange(range);
                    }
                }
            }
            else {
                text = await navigator.clipboard.readText();
            }
            _this.listen({ name: 'pasted', params: [text] });
            if (this === _this.contEl) {
                _this.inputEl.value = _this.contEl.innerHTML;
            }
            else if (this === _this.inputEl) {
                _this.contEl.innerHTML = _this.inputEl.value;
            }
            _this.output.value = _this.inputEl.value;
            _this.updateInfo();
        };
        this.readonlyEvt = (e) => {
            e.preventDefault();
        };
        this.tools = [
            ...this.createToggleItems([
                { name: 'bold', icon: `${prefix}icon-bold`, key: 'fontWeight', value: 'bold', tips: this.options.lang.tips.bold },
                { name: 'italic', icon: `${prefix}icon-italic`, key: 'fontStyle', value: 'italic', tips: this.options.lang.tips.italic },
                { name: 'underline', icon: `${prefix}icon-underline`, key: 'textDecorationLine', value: 'underline', tips: this.options.lang.tips.underline },
                { name: 'through', icon: `${prefix}icon-through`, key: 'textDecorationLine', value: 'line-through', tips: this.options.lang.tips.through },
            ]),
            ...this.createFontsizeItems([
                { name: 'fontsize-dft', label: this.options.lang.fontsizeDft, value: '' },
                { name: '1.2rem', label: '12px/1.2rem', value: '1.2rem' },
                { name: '1.4rem', label: '14px/1.4rem', value: '1.4rem' },
                { name: '1.6rem', label: '16px/1.6rem', value: '1.6rem' },
                { name: '1.8rem', label: '18px/1.8rem', value: '1.8rem' },
                { name: '2.2rem', label: '22px/2.2rem', value: '2.2rem' },
                { name: '2.8rem', label: '2.8px/2.8rem', value: '2.8rem' },
                { name: '3.2rem', label: '32px/3.2rem', value: '3.2rem' },
                { name: '3.8rem', label: '38px/3.8rem', value: '3.8rem' },
                { name: '4.2rem', label: '42px/4.2rem', value: '4.2rem' },
                { name: '4.8rem', label: '48px/4.8rem', value: '4.8rem' },
            ]),
            ...this.createAlignItems([
                { name: 'align-dft', label: this.options.lang.alignDft, value: '' },
                { name: 'align-left', icon: `${prefix}icon-align-left`, value: 'left', tips: this.options.lang.tips.alignLeft },
                { name: 'align-center', icon: `${prefix}icon-align-center`, value: 'center', tips: this.options.lang.tips.alignCenter },
                { name: 'align-right', icon: `${prefix}icon-align-right`, value: 'right', tips: this.options.lang.tips.alignRight },
                { name: 'align-justify', icon: `${prefix}icon-align-justify`, value: 'justify', tips: this.options.lang.tips.alignJustify },
            ]),
            ...this.createSpanTags([
                { name: 'sup', icon: `${prefix}icon-superscript`, value: 'sup', tips: this.options.lang.tips.sup },
                { name: 'sub', icon: `${prefix}icon-subscript`, value: 'sub', tips: this.options.lang.tips.sub },
                { name: 'highlight', icon: `${prefix}icon-highlight`, value: 'mark', tips: this.options.lang.tips.highlight },
                { name: 'cite', icon: `${prefix}icon-quote-left`, value: 'cite', tips: this.options.lang.tips.cite },
                { name: 'code-inline', icon: `${prefix}icon-code-inline`, value: 'code', tips: this.options.lang.tips.codeInline },
                { name: 'em', label: this.options.lang.tips.em, value: 'em', },
                { name: 'address', label: this.options.lang.tips.address, value: 'address', },
                { name: 'time', label: this.options.lang.tips.time, value: 'time', },
                { name: 'ruby', label: this.options.lang.tips.ruby, value: 'ruby', },
                { name: 'rt', label: this.options.lang.tips.rt, value: 'rt', },
            ]),
            ...this.createLineTags([
                { name: 'p', icon: `${prefix}icon-article`, value: 'p', tips: this.options.lang.tips.text, },
                { name: 'h1', icon: `${prefix}icon-heading-1`, value: 'h1', tips: this.options.lang.tips.h1 },
                { name: 'h2', icon: `${prefix}icon-heading-2`, value: 'h2', tips: this.options.lang.tips.h2 },
                { name: 'h3', icon: `${prefix}icon-heading-3`, value: 'h3', tips: this.options.lang.tips.h3 },
                { name: 'h4', icon: `${prefix}icon-heading-4`, value: 'h4', tips: this.options.lang.tips.h4 },
                { name: 'h5', icon: `${prefix}icon-heading-5`, value: 'h5', tips: this.options.lang.tips.h5 },
                { name: 'h6', icon: `${prefix}icon-heading-6`, value: 'h6', tips: this.options.lang.tips.h6 },
                { name: 'blockquote', icon: `${prefix}icon-quote-left-f`, value: 'blockquote', tips: this.options.lang.tips.blockquote },
            ]),
            ...this.createCloseTags([
                { name: 'hr', icon: `${prefix}icon-line-h`, value: 'hr', tips: this.options.lang.tips.hr },
                { name: 'br', icon: `${prefix}icon-br`, value: 'br', tips: this.options.lang.tips.br },
            ]),
            ...this.createNestTags([
                { name: 'code-block', icon: `${prefix}icon-code-block`, value: ['pre', 'code'], tips: this.options.lang.tips.codeBlock, },
                { name: 'list-ul', icon: `${prefix}icon-list-ul`, value: ['ul', 'li', 'br'], tips: this.options.lang.tips.listUl },
                { name: 'list-ol', icon: `${prefix}icon-list-ol`, value: ['ol', 'li', 'br'], tips: this.options.lang.tips.listOl },
                { name: 'paragraph', icon: `${prefix}icon-paragraph`, value: ['div', 'br'], tips: this.options.lang.tips.paragraph, isRoot: true },
            ]),
            {
                name: 'indent-more',
                icon: `${prefix}icon-indent-more`,
                tips: this.options.lang.tips.indentMore,
                action: (resp) => {
                    resp.wrapEl.onpointerdown = (e) => {
                        e.preventDefault();
                        this.setLineStyles('', '', (el) => {
                            let now = style(el).textIndent;
                            el.style.textIndent = `${toPixel(now) + toPixel('2.8rem')}px`;
                        });
                    };
                }
            },
            {
                name: 'indent-less',
                icon: `${prefix}icon-indent-less`,
                tips: this.options.lang.tips.indentLess,
                action: (resp) => {
                    resp.wrapEl.onpointerdown = (e) => {
                        e.preventDefault();
                        this.setLineStyles('', '', (el) => {
                            let now = style(el).textIndent, val = Math.max(0, toPixel(now) - toPixel('2.8rem'));
                            el.style.textIndent = `${val}px`;
                        });
                    };
                }
            },
            {
                name: 'dropdown-fontsize',
                label: this.options.lang.fontsizeDft,
                extendable: true,
                action: (resp) => {
                    let content = [
                        { label: this.options.lang.fontsizeDft, value: '' },
                        { label: '12px/1.2rem', value: '1.2rem' },
                        { label: '14px/1.4rem', value: '1.4rem' },
                        { label: '16px/1.6rem', value: '1.6rem' },
                        { label: '18px1.8rem', value: '1.8rem' },
                        { label: '22px/2.2rem', value: '2.2rem' },
                        { label: '28px/2.8rem', value: '2.8rem' },
                        { label: '32px/3.2rem', value: '3.2rem' },
                        { label: '38px/3.8rem', value: '3.8rem' },
                        { label: '42px/4.2rem', value: '4.2rem' },
                        { label: '48px/4.8rem', value: '4.8rem' },
                    ];
                    new Dropdown(resp.wrapEl, {
                        content: content.map((k) => k.label),
                        feature: 'select',
                        popup: {
                            onTargetSet: (value) => {
                                this.setStyles('fontSize', content.find((k) => k.label === value).value);
                            }
                        }
                    });
                }
            },
            {
                name: 'dropdown-align',
                label: this.options.lang.alignDft,
                extendable: true,
                action: (resp) => {
                    let content = [
                        { label: this.options.lang.alignDft, value: '' },
                        { label: this.options.lang.tips.alignLeft, icon: `${prefix}icon-align-left`, value: 'left' },
                        { label: this.options.lang.tips.alignCenter, icon: `${prefix}icon-align-center`, value: 'center' },
                        { label: this.options.lang.tips.alignRight, icon: `${prefix}icon-align-right`, value: 'right' },
                        { label: this.options.lang.tips.alignJustify, icon: `${prefix}icon-align-justify`, value: 'justify' },
                    ];
                    new Dropdown(resp.wrapEl, {
                        content: content.map((k) => k.label),
                        feature: 'select',
                        popup: {
                            onTargetSet: (value) => {
                                this.setLineStyles('textAlign', content.find((k) => k.label === value).value);
                            }
                        }
                    });
                }
            },
            {
                name: 'dropdown-heading',
                label: this.options.lang.tips.text,
                extendable: true,
                action: (resp) => {
                    let content = [
                        { label: this.options.lang.tips.text, value: 'p' },
                        { label: this.options.lang.tips.h1, icon: `${prefix}icon-heading-1`, value: 'h1' },
                        { label: this.options.lang.tips.h2, icon: `${prefix}icon-heading-2`, value: 'h2' },
                        { label: this.options.lang.tips.h3, icon: `${prefix}icon-heading-3`, value: 'h3' },
                        { label: this.options.lang.tips.h4, icon: `${prefix}icon-heading-1`, value: 'h4' },
                        { label: this.options.lang.tips.h5, icon: `${prefix}icon-heading-5`, value: 'h5' },
                        { label: this.options.lang.tips.h6, icon: `${prefix}icon-heading-6`, value: 'h6' },
                    ];
                    new Dropdown(resp.wrapEl, {
                        content: content.map((k) => k.label),
                        feature: 'select',
                        popup: {
                            onTargetSet: (value) => {
                                this.replaceLineTag(content.find((k) => k.label === value).value);
                            }
                        }
                    });
                }
            },
            {
                name: 'dropdown-tags',
                label: this.options.lang.tagsDft,
                extendable: true,
                action: (resp) => {
                    let content = [
                        { label: this.options.lang.tips.em, value: 'em' },
                        { label: this.options.lang.tips.address, value: 'address' },
                        { label: this.options.lang.tips.time, value: 'time' },
                        { label: this.options.lang.tips.ruby, value: 'ruby' },
                        { label: this.options.lang.tips.rt, value: 'rt' },
                    ];
                    new Dropdown(resp.wrapEl, {
                        content: content.map((k) => k.label),
                        feature: 'button',
                        popup: { trigger: 'hover' },
                        action: (resp) => {
                            resp.el.onclick = (e) => {
                                e.preventDefault();
                                this.addWraps(content.find((k) => k.label === resp.data.label).value);
                            };
                        }
                    });
                }
            },
            {
                name: 'source',
                icon: `${prefix}icon-code-box`,
                swap: `${prefix}icon-html-box`,
                tips: this.options.lang.tips.source,
                action: (resp) => {
                    resp.wrapEl.onpointerdown = (e) => {
                        e.preventDefault();
                        if (this.targetEl.getAttribute('mode') === 'source') {
                            this.targetEl.setAttribute('mode', 'editor');
                            resp.wrapEl.toggleAttribute('focused', false);
                            classes(resp.iconEl).replace(resp.icon, resp.swap);
                        }
                        else {
                            this.targetEl.setAttribute('mode', 'source');
                            resp.wrapEl.toggleAttribute('focused', true);
                            classes(resp.iconEl).replace(resp.swap, resp.icon);
                        }
                    };
                }
            },
        ];
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch (err) {
            err ? console.error(err) : console.warn(config.warn.init);
            return this;
        }
        this.output = { value: '', raw: '' };
        this.tmpRaw = '';
        super.useTpl();
        this.renderMain();
        this.setAttrs();
        !this.options.deferred && await this.fillCont(this.options.content || this.options.value || this.rawHtml);
        this.renderFinish();
        this.contEl.addEventListener("input", this.inputEvt, false);
        this.inputEl.addEventListener("input", this.inputEvt, false);
        this.contEl.addEventListener("paste", this.pasteEvt, false);
        this.inputEl.addEventListener("paste", this.pasteEvt, false);
        this.contEl.addEventListener("keyup", this.keyupEvt, false);
        this.contEl.addEventListener("pointerdown", this.pointerEvt, false);
        super.listen({ name: 'initiated', cb });
        return this;
    }
    insertNewPara(parentEl, newPara, startNode) {
        if (parentEl === this.contEl) {
            this.contEl.insertBefore(newPara, startNode === this.contEl || !startNode ? null : startNode.nextSibling);
        }
        else {
            parentEl.parentNode.insertBefore(newPara, parentEl.nextSibling);
        }
    }
    getTools() {
        if (!isEmpty(this.options.header.children)) {
            return this.options.header.children.map((k) => {
                let tmp = this.tools.find((i) => i.name === k);
                return tmp || k;
            });
        }
        else {
            if (this.options.feature === 'simple') {
                return ['bold', 'align-left', 'align-center', 'align-right', 'highlight', 'p', 'h1', 'h2', 'h3', 'hr', 'br', 'paragraph', 'indent-more', 'indent-less', 'source']
                    .map((k) => this.tools.find((i) => i.name === k));
            }
            else if (this.options.feature === 'comm') {
                return ['bold', 'italic', 'underline', 'through', 'align-left', 'align-center', 'align-right', 'highlight', 'cite', 'blockquote',
                    'code-inline', 'code-block', 'p', 'h1', 'h2', 'h3', 'hr', 'br', 'list-ul', 'list-ol', 'paragraph', 'indent-more', 'indent-less', 'source']
                    .map((k) => this.tools.find((i) => i.name === k));
            }
            else if (this.options.feature === 'flat') {
                return this.tools.filter((k) => !k.name.includes('dropdown-') && !k.name.includes('group-'));
            }
            else if (this.options.feature === 'group') {
                return this.tools.filter((k) => k.name.includes('dropdown-') || k.name.includes('group-'));
            }
            else {
                return this.tools;
            }
        }
    }
    renderMain() {
        this.targetEl.innerHTML = '';
        this.headEl = createEl('div', { class: `${prefix}editor-head` });
        this.inputEl.placeholder = this.options.lang.placeholder;
        this.options.name && (this.inputEl.name = this.options.name);
        this.bodyEl = createEl('div', { class: `${prefix}editor-body` });
        this.bodyEl.append(this.contEl, this.inputEl);
        this.pathEl = createEl('div', { class: `${prefix}editor-path` });
        this.stateEl = createEl('div', { class: `${prefix}editor-state` });
        this.footEl = createEl('div', { class: `${prefix}editor-foot` });
        this.footEl.append(this.pathEl, this.stateEl);
        this.maskEl = createEl('div', { class: `${prefix}editor-mask` }, this.options.lang.defer);
        if (this.options.header.enable) {
            this.toolsEl = createTools(this.getTools(), this.headEl);
            this.targetEl.appendChild(this.headEl);
        }
        this.targetEl.appendChild(this.bodyEl);
        this.options.footer.enable && this.targetEl.appendChild(this.footEl);
        this.options.deferred && this.bodyEl.appendChild(this.maskEl);
        this.updateInfo();
    }
    updateInfo() {
        this.setPathInfo();
        this.setStateInfo();
        this.output = { value: this.inputEl.value, raw: this.tmpRaw };
        super.listen({ name: 'output', params: [this.output] });
        this.tmpRaw = this.inputEl.value;
    }
    setPathInfo(selection = document.getSelection()) {
        if (!selection.rangeCount) {
            this.pathEl.innerHTML = this.options.lang.path + ' -';
            return;
        }
        let range = selection.getRangeAt(0), startNode = range.startContainer, parNames = [];
        if (!this.isValidRange(startNode))
            return;
        while (startNode && this.isValidRange(startNode)) {
            parNames.unshift(startNode.nodeName.toLowerCase());
            startNode = startNode.parentNode;
        }
        this.pathEl.innerHTML = this.options.lang.path + ' ' + parNames.join(' › ');
    }
    setStateInfo() {
        let charCount = this.contEl.textContent.replace(/\s/g, '').length, paraCount = this.contEl.childNodes.length, html = this.contEl.innerHTML;
        this.stateEl.innerHTML = this.options.lang.chars + ' ' + +charCount + ' , ' + this.options.lang.paras + ' ' + +paraCount;
        super.updateCache({ content: html });
        super.listen({ name: 'edited', params: [html] });
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}editor`);
        this.options.classes && classes(this.targetEl).add(this.options.classes);
        this.options.readonly ? this.readonly() : this.enable();
        this.targetEl.toggleAttribute('inert', this.options.disabled);
        this.targetEl.setAttribute('mode', this.options.mode);
        this.options.minHeight ? addStyle(this.contEl, 'max-height', this.options.minHeight) : removeStyle(this.contEl, 'max-height');
        for (let k in this.options.appear) {
            let val = this.options.appear[k];
            this.targetEl.toggleAttribute(k, val);
        }
        this.targetEl.toggleAttribute('deferred', this.options.deferred);
    }
    readonly() {
        this.headEl.toggleAttribute('inert', true);
        this.contEl.addEventListener('keydown', this.readonlyEvt);
        this.contEl.addEventListener('paste', this.readonlyEvt);
        this.contEl.addEventListener('drop', this.readonlyEvt, false);
        this.inputEl.addEventListener('keydown', this.readonlyEvt, false);
        this.inputEl.addEventListener('paste', this.readonlyEvt, false);
        this.inputEl.addEventListener('drop', this.readonlyEvt, false);
        this.inputEl.readOnly = true;
        super.listen({ name: 'readonly' });
    }
    disable() {
        this.headEl.toggleAttribute('inert', true);
        this.targetEl.toggleAttribute('inert', true);
        this.contEl.addEventListener('keydown', this.readonlyEvt, false);
        this.contEl.addEventListener('paste', this.readonlyEvt, false);
        this.contEl.addEventListener('drop', this.readonlyEvt, false);
        this.inputEl.addEventListener('keydown', this.readonlyEvt, false);
        this.inputEl.addEventListener('paste', this.readonlyEvt, false);
        this.inputEl.addEventListener('drop', this.readonlyEvt, false);
        this.inputEl.readOnly = true;
        super.listen({ name: 'disabled' });
    }
    enable() {
        this.headEl.toggleAttribute('inert', false);
        this.targetEl.toggleAttribute('inert', false);
        this.contEl.removeEventListener('keydown', this.readonlyEvt);
        this.contEl.removeEventListener('paste', this.readonlyEvt);
        this.contEl.removeEventListener('drop', this.readonlyEvt);
        this.inputEl.removeEventListener('keydown', this.readonlyEvt);
        this.inputEl.removeEventListener('paste', this.readonlyEvt);
        this.inputEl.removeEventListener('drop', this.readonlyEvt);
        this.inputEl.readOnly = false;
        super.listen({ name: 'enabled' });
    }
    renderFinish() {
        if (this.options.deferred) {
            this.maskEl.onclick = async () => {
                await this.fillCont(this.options.content || this.options.value);
                this.targetEl.removeAttribute('deferred');
                this.maskEl.remove();
            };
        }
    }
    fullTextNode(range) {
        let startNode = range.startContainer, endNode = range.endContainer;
        if (startNode === endNode) {
            if (startNode.nodeType === 3) {
                const text = startNode.textContent;
                return range.startOffset === 0 && range.endOffset === text.length;
            }
            else if (startNode.nodeType === 1) {
                return range.startOffset === 0 && range.endOffset === 1;
            }
            else {
                return false;
            }
        }
        else {
            return false;
        }
    }
    setChildRange(wrap, selection = document.getSelection()) {
        let newRange = document.createRange();
        if (!wrap.firstChild || !wrap.lastChild)
            return;
        newRange.setStartBefore(wrap.firstChild);
        newRange.setEndAfter(wrap.lastChild);
        selection.removeAllRanges();
        selection.addRange(newRange);
    }
    setStyles(key, value, selection = document.getSelection()) {
        if (selection.rangeCount > 0) {
            for (let i = 0; i < selection.rangeCount; i++) {
                let range = selection.getRangeAt(i), startNode = range.startContainer;
                if (!this.isValidRange(startNode))
                    continue;
                let wrap = this.fullTextNode(range) ? (startNode.nodeType === 1 ? startNode : startNode.parentNode) : this.addOneWrap('i', range);
                wrap.style[key] = value;
                this.setChildRange(wrap, selection);
            }
            this.updateInputVal();
        }
    }
    getParentNode(range) {
        let parent = range.commonAncestorContainer;
        return (parent.nodeType !== 1) ? parent.parentNode : parent;
    }
    removeStyles(key, selection = document.getSelection()) {
        if (selection.rangeCount > 0) {
            for (let i = 0; i < selection.rangeCount; i++) {
                let range = selection.getRangeAt(i), startNode = range.startContainer;
                if (!this.isValidRange(startNode))
                    continue;
                let parent = this.getParentNode(range);
                if (parent) {
                    parent.style[key] = null;
                }
            }
        }
    }
    toggleStyles(key, value, selection = document.getSelection()) {
        if (selection.rangeCount > 0) {
            for (let i = 0; i < selection.rangeCount; i++) {
                let range = selection.getRangeAt(i), startNode = range.startContainer;
                if (!this.isValidRange(startNode))
                    continue;
                let parent = this.getParentNode(range);
                if (parent && parent.style[key]) {
                    parent.style[key] = null;
                }
                else {
                    let wrap = this.fullTextNode(range) ? (startNode.nodeType === 1 ? startNode : startNode.parentNode) : this.addOneWrap('i', range);
                    wrap.style[key] = value;
                    this.setChildRange(wrap, selection);
                }
            }
            this.updateInputVal();
        }
    }
    addOneWrap(tagName, range) {
        let text = range.extractContents(), wrap = document.createElement(tagName);
        wrap.appendChild(text);
        range.insertNode(wrap);
        return wrap;
    }
    addWraps(tagName, selection = document.getSelection()) {
        if (!selection.rangeCount)
            return;
        let last;
        for (let i = 0; i < selection.rangeCount; i++) {
            let range = selection.getRangeAt(i), startNode = range.startContainer;
            if (!this.isValidRange(startNode))
                continue;
            let parent = this.getParentNode(range);
            if (parent && parent.nodeName === tagName.toUpperCase()) {
                continue;
            }
            let wrap = this.addOneWrap(tagName, range);
            if (i === selection.rangeCount - 1) {
                last = wrap;
            }
        }
        last && this.setChildRange(last, selection);
        this.updateInputVal();
    }
    replaceLineTag(tagName, selection = document.getSelection()) {
        if (!selection.rangeCount)
            return;
        let range = selection.getRangeAt(0), startNode = range.startContainer, newTag = document.createElement(tagName), startOffset = range.startOffset, newRange = document.createRange();
        if (!this.isValidRange(startNode))
            return;
        while (startNode && startNode.nodeType === 3 && startNode.parentNode !== this.contEl)
            startNode = startNode.parentNode;
        if (startNode?.tagName?.toLowerCase() === tagName)
            return;
        newTag.innerHTML = startNode.innerHTML || startNode.textContent;
        startNode.parentNode.replaceChild(newTag, startNode);
        let start = newTag.firstChild || newTag;
        try {
            newRange.setStart(start, startOffset);
        }
        catch {
            newRange.setStart(start, 0);
        }
        newRange.collapse(true);
        selection.removeAllRanges();
        selection.addRange(newRange);
        this.updateInputVal();
    }
    setLineStyles(key, value, cb) {
        let selection = document.getSelection(), range = selection.getRangeAt(0), startNode = range.startContainer, parEl = startNode?.parentNode;
        if (!selection.rangeCount || !this.isValidRange(startNode) || parEl === this.contEl)
            return;
        key && (parEl.style[key] = value);
        this.updateInputVal();
        cb && cb(parEl);
    }
    createNestWrap(tags) {
        let root = document.createElement(tags[0]), leaf;
        tags.slice(1).reduce((parent, tag, idx) => {
            let newNode = document.createElement(tag);
            if (idx === tags.length - 2)
                leaf = newNode;
            parent.appendChild(newNode);
            return newNode;
        }, root);
        return { root, leaf };
    }
    insertNestNodes(tags, isRoot = false) {
        if (!Array.isArray(tags) || !tags.length)
            return;
        let selection = document.getSelection();
        if (!selection)
            return;
        let range = selection.getRangeAt(0), startNode = range.startContainer, { root, leaf } = this.createNestWrap(tags), newRange = document.createRange();
        if (!selection.rangeCount || !this.isValidRange(startNode, false))
            return;
        if (isRoot) {
            let parNode = startNode, curNode = startNode;
            while (parNode !== this.contEl) {
                curNode = parNode;
                parNode = parNode.parentNode;
            }
            this.contEl.insertBefore(root, curNode === this.contEl ? null : curNode.nextSibling);
        }
        else {
            range.insertNode(root);
        }
        newRange.setStart(leaf, 0);
        newRange.collapse(true);
        selection.removeAllRanges();
        selection.addRange(newRange);
        this.updateInputVal();
    }
    insertTag(tag) {
        let selection = document.getSelection();
        if (!selection.rangeCount || !this.isValidRange(null, false))
            return;
        let range = selection.getRangeAt(0), node = document.createElement(tag);
        this.clearRanges(selection);
        range.insertNode(node);
        range.setStartAfter(node);
        range.setEndAfter(node);
        selection.removeAllRanges();
        selection.addRange(range);
        this.updateInputVal();
    }
    clearRanges(selection = document.getSelection()) {
        if (selection.rangeCount > 0) {
            for (let i = 0; i < selection.rangeCount; i++) {
                let range = selection.getRangeAt(i);
                range.deleteContents();
            }
        }
    }
    isValidRange(node, exact = true) {
        let startNode;
        if (!node) {
            let selection = document.getSelection(), range = selection.getRangeAt(0);
            startNode = range.startContainer;
        }
        else {
            startNode = node;
        }
        return this.contEl.contains(startNode) && (exact ? this.contEl !== startNode : true);
    }
    createFontsizeItems(arr) {
        return arr.map((k) => {
            return {
                name: k.name,
                label: k.label,
                tips: k.tips,
                icon: k.icon,
                action: (resp) => {
                    resp.wrapEl.onpointerdown = (e) => {
                        e.preventDefault();
                        this.setStyles('fontSize', k.value);
                    };
                }
            };
        });
    }
    createToggleItems(arr) {
        return arr.map((k) => {
            return {
                name: k.name,
                label: k.label,
                tips: k.tips,
                icon: k.icon,
                action: (resp) => {
                    resp.wrapEl.onpointerdown = (e) => {
                        e.preventDefault();
                        this.toggleStyles(k.key, k.value);
                    };
                }
            };
        });
    }
    createAlignItems(arr) {
        return arr.map((k) => {
            return {
                name: k.name,
                label: k.label,
                tips: k.tips,
                icon: k.icon,
                action: (resp) => {
                    resp.wrapEl.onpointerdown = (e) => {
                        e.preventDefault();
                        this.setLineStyles('textAlign', k.value);
                    };
                }
            };
        });
    }
    createLineTags(arr) {
        return arr.map((k) => {
            return {
                name: k.name,
                label: k.label,
                tips: k.tips,
                icon: k.icon,
                action: (resp) => {
                    resp.wrapEl.onpointerdown = (e) => {
                        e.preventDefault();
                        this.replaceLineTag(k.value);
                    };
                }
            };
        });
    }
    createSpanTags(arr) {
        return arr.map((k) => {
            return {
                name: k.name,
                label: k.label,
                tips: k.tips,
                icon: k.icon,
                action: (resp) => {
                    resp.wrapEl.onpointerdown = (e) => {
                        e.preventDefault();
                        this.addWraps(k.value);
                    };
                }
            };
        });
    }
    createCloseTags(arr) {
        return arr.map((k) => {
            return {
                name: k.name,
                label: k.label,
                tips: k.tips,
                icon: k.icon,
                action: (resp) => {
                    resp.wrapEl.onpointerdown = (e) => {
                        e.preventDefault();
                        this.insertTag(k.value);
                    };
                }
            };
        });
    }
    createNestTags(arr) {
        return arr.map((k) => {
            return {
                name: k.name,
                label: k.label,
                tips: k.tips,
                icon: k.icon,
                action: (resp) => {
                    resp.wrapEl.onpointerdown = (e) => {
                        e.preventDefault();
                        this.insertNestNodes(k.value, k.isRoot);
                    };
                }
            };
        });
    }
    async fillCont(content = this.options.content, cb) {
        if (isEmpty(content))
            return;
        let result, spinProp = elState(this.maskEl).isVisible ? { target: this.maskEl } : { spinSel: this.bodyEl };
        await getContent.call(this, {
            content,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: {
                xhrName: 'contXhr',
                ...spinProp,
                ...this.options.ajax
            },
            request: (data) => {
                super.listen({ name: 'request', params: [data] });
            },
            cb: async (resp) => {
                result = resp;
                setContent({
                    content: resp,
                    template: content?.tplStr || this.tplStr,
                    engine: content?.tplEng || this.tplEng,
                    target: this.contEl,
                    prevent: (result) => {
                        if (getDataType(result).includes('HTML')) {
                            this.contEl.appendChild(result);
                        }
                        else {
                            this.contEl.innerHTML = result;
                        }
                        this.inputEl.value = this.output.value = this.contEl.innerHTML;
                        this.updateInfo();
                    }
                });
            }
        });
        super.listen({ name: 'filled', cb, params: [result] });
    }
    updateInputVal() {
        this.inputEl.value = this.output.value = this.contEl.innerHTML;
        this.updateInfo();
    }
    setVals(value, cb) {
        if (this.destroyed)
            return this;
        this.inputEl.value = this.contEl.innerHTML = value;
        this.updateInfo();
        super.listen({ name: 'set', cb, params: [value] });
        return this;
    }
    getVals() {
        if (this.destroyed)
            return;
        return this.contEl.innerHTML;
    }
    clearVals(cb) {
        if (this.destroyed)
            return;
        this.inputEl.value = this.contEl.innerHTML = '';
        this.updateInfo();
        super.listen({ name: 'cleared', cb });
        return this;
    }
    
    destroy(cb) {
        if (this.destroyed)
            return this;
        this.contEl.removeEventListener("input", this.inputEvt);
        this.inputEl.removeEventListener("input", this.inputEvt);
        this.contEl.removeEventListener("keyup", this.keyupEvt);
        this.enable();
        this.contXhr && this.contXhr.abort();
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

const optSelect = [
    {
        attr: 'name',
        prop: 'name',
        value: '',
    },
    {
        attr: 'value',
        prop: 'value',
        value: '',
    },
    {
        attr: 'field',
        prop: 'field',
        value: 'label',
    },
    {
        attr: 'type',
        prop: 'type',
        value: 'dropdown',
    },
    {
        attr: 'manual',
        prop: 'manual',
        value: false,
    },
    {
        attr: 'content',
        prop: 'content',
        value: '',
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'disabled',
        prop: 'disabled',
        value: false,
    },
    {
        attr: 'multiple',
        prop: 'multiple',
        value: false,
    },
    {
        attr: 'exclude',
        prop: 'exclude',
        value: '',
    },
    {
        attr: 'readonly',
        prop: 'readonly',
        value: false,
    },
    {
        attr: 'full',
        prop: 'full',
        value: false,
    },
    {
        attr: 'min',
        prop: 'min',
        value: 0,
    },
    {
        attr: 'max',
        prop: 'max',
        value: 0,
    },
    {
        attr: 'sliced',
        prop: 'sliced',
        value: true,
    },
    {
        attr: 'removable',
        prop: 'removable',
        value: true,
    },
    {
        attr: 'max-height',
        prop: 'maxHeight',
        value: '',
    },
    {
        attr: 'span',
        prop: 'span',
        value: 'leaf',
    },
    {
        attr: 'unique',
        prop: 'unique',
        value: true,
    },
    {
        attr: 'collapse',
        prop: 'collapse',
        value: false,
    },
    {
        attr: 'size',
        prop: 'size',
        value: 'md',
    },
    {
        attr: 'search',
        prop: 'search',
        value: {
            enable: false,
            value: '',
            fuzzy: true,
            ignore: true,
            result: true,
        },
    },
    {
        attr: 'tools',
        prop: 'tools',
        value: {
            enable: false,
            children: ['close']
        },
    },
    {
        attr: 'auto-width',
        prop: 'autoWidth',
        value: true,
    },
    {
        attr: 'status',
        prop: 'status',
        value: false,
    },
    {
        attr: 'tags',
        prop: 'tags',
        value: {},
    },
    {
        attr: 'popup',
        prop: 'popup',
        value: {
            enable: true,
        },
    },
    {
        attr: 'tree',
        prop: 'tree',
        value: {},
    },
    {
        attr: 'b4-expand',
        prop: 'b4Expand',
        value: null,
    },
    {
        attr: 'b4-collapse',
        prop: 'b4Collapse',
        value: null,
    },
    {
        attr: 'b4-add',
        prop: 'b4Add',
        value: null,
    },
    {
        attr: 'b4-edit',
        prop: 'b4Edit',
        value: null,
    },
    {
        attr: 'b4-remove',
        prop: 'b4Remove',
        value: null,
    },
    {
        attr: 'b4-graft',
        prop: 'b4Graft',
        value: null,
    },
    {
        attr: 'on-rendered',
        prop: 'onRendered',
        value: null,
    },
    {
        attr: 'on-trigger',
        prop: 'onTrigger',
        value: null
    },
    {
        attr: 'on-added',
        prop: 'onAdded',
        value: null
    },
    {
        attr: 'on-edited',
        prop: 'onEdited',
        value: null
    },
    {
        attr: 'on-removed',
        prop: 'onRemoved',
        value: null
    },
    {
        attr: 'on-disabled',
        prop: 'onDisabled',
        value: null
    },
    {
        attr: 'on-enabled',
        prop: 'onEnabled',
        value: null
    },
    {
        attr: 'on-disabledall',
        prop: 'onDisabledAll',
        value: null
    },
    {
        attr: 'on-enabledall',
        prop: 'onEnabledAll',
        value: null
    },
    {
        attr: 'on-readonly',
        prop: 'onReadonly',
        value: null
    },
    {
        attr: 'on-readonlyall',
        prop: 'onReadonlyAll',
        value: null
    },
    {
        attr: 'on-expand',
        prop: 'onExpand',
        value: null
    },
    {
        attr: 'on-expanded',
        prop: 'onExpanded',
        value: null
    },
    {
        attr: 'on-collapse',
        prop: 'onCollapse',
        value: null
    },
    {
        attr: 'on-collapsed',
        prop: 'onCollapsed',
        value: null
    },
    {
        attr: 'on-expandall',
        prop: 'onExpandAll',
        value: null
    },
    {
        attr: 'on-expandedAll',
        prop: 'onExpandedAll',
        value: null
    },
    {
        attr: 'on-collapsedall',
        prop: 'onCollapsedAll',
        value: null
    },
    {
        attr: 'on-selected',
        prop: 'onSelected',
        value: null
    },
    {
        attr: 'on-deselected',
        prop: 'onDeselected',
        value: null
    },
    {
        attr: 'on-selectedall',
        prop: 'onSelectedAll',
        value: null
    },
    {
        attr: 'on-checked',
        prop: 'onChecked',
        value: null
    },
    {
        attr: 'on-unchecked',
        prop: 'onUnchecked',
        value: null
    },
    {
        attr: 'on-checkedall',
        prop: 'onCheckedAll',
        value: null
    },
    {
        attr: 'on-filled',
        prop: 'onFilled',
        value: null
    },
    {
        attr: 'on-turned',
        prop: 'onTurned',
        value: null
    },
    {
        attr: 'on-got',
        prop: 'onGot',
        value: null
    },
    {
        attr: 'on-set',
        prop: 'onSet',
        value: null
    },
    {
        attr: 'on-cleared',
        prop: 'onCleared',
        value: null
    },
    {
        attr: 'on-grafted',
        prop: 'onGrafted',
        value: null
    },
    {
        attr: 'on-updatedcont',
        prop: 'onUpdatedCont',
        value: null
    },
    {
        attr: 'on-request',
        prop: 'onRequest',
        value: null
    },
    {
        attr: 'on-toofew',
        prop: 'onTooFew',
        value: null
    },
    {
        attr: 'on-toomany',
        prop: 'onTooMany',
        value: null
    },
    {
        attr: 'on-locked',
        prop: 'onLocked',
        value: null
    },
    {
        attr: 'on-unlocked',
        prop: 'onUnlocked',
        value: null
    },
    {
        attr: 'on-output',
        prop: 'onOutput',
        value: null
    },
    ...optBase
];

class Select extends ModBaseListenCache {
    options = {};
    flatData;
    selectData;
    inputEl;
    tagsEl;
    arrowEl;
    toolsEl;
    listEl;
    wrapEl;
    searchEl;
    resultEl;
    keysEl;
    checkEl;
    statsEl;
    statusEl;
    statusHolderEl;
    popupIns;
    tagsIns;
    treeIns;
    output;
    rawHmtl;
    clearVals;
    static hostType = 'node';
    static optMaps = optSelect;
    constructor(elem, options = {}, initial = true) {
        super();
        let tmp = getEl(elem), host;
        if (!tmp)
            return this;
        if (['SELECT', 'INPUT'].includes(tmp.nodeName)) {
            this.inputEl = tmp;
            host = createEl('div', { class: `${prefix}select` });
            tmp.insertAdjacentElement('afterend', host);
            host.appendChild(this.inputEl);
        }
        else {
            host = tmp;
            host.classList.add(`${prefix}select`);
            this.inputEl = createEl('input', { type: 'hidden' });
        }
        super.ready({
            options,
            host,
            maps: Select.optMaps,
            component: true,
            spread: ['popup', 'search', 'tools']
        });
        
        
        
        
        
        
        
        
        
        
        
        
        
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch (err) {
            err ? console.error(err) : console.warn(config.warn.init);
            return this;
        }
        this.correctOpts();
        super.useTpl();
        this.renderMain();
        this.setAttrs();
        this.output = { value: '', raw: '', items: [] };
        if (this.options.popup.enable && !this.options.readonly) {
            this.setPopup();
            await this.popupIns.init();
        }
        else {
            this.popupIns && this.popupIns.destroy();
        }
        this.setTags();
        await this.tagsIns.init();
        this.setTree();
        await this.treeIns.init();
        this.renderFinish();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    renderMain() {
        this.targetEl.innerHTML = '';
        this.inputEl.setAttribute([alias], 'input');
        let nameTmp = this.options.name;
        if (this.inputEl.nodeName === 'SELECT' && this.options.multiple)
            nameTmp += `[]`;
        this.options.name && (this.inputEl.name = nameTmp);
        this.targetEl.appendChild(this.inputEl);
        this.tagsEl = createEl('div', { [alias]: 'tags' });
        this.arrowEl = createEl('i', { [alias]: 'arrow' });
        this.targetEl.appendChild(this.tagsEl);
        if (this.options.tools.enable) {
            this.toolsEl = createTools(this.options.tools.children, null, this);
            this.tagsEl.insertAdjacentElement('afterend', this.toolsEl);
            this.toolsEl.setAttribute(alias, 'tools');
            for (let k of this.options.tools.children) {
                !k.tips && !k.title && this.options.lang.title[k.name] && k.wrapEl.setAttribute('title', this.options.lang.title[k.name]);
                if (k.name === 'close') {
                    k.wrapEl.onclick = () => {
                        this.clearVals();
                    };
                }
            }
        }
        this.options.popup.enable && this.targetEl.appendChild(this.arrowEl);
        this.listEl = createEl('div', { class: `${prefix}select-list` });
        this.wrapEl = createEl('div', { class: `${prefix}select-wrap` }, this.listEl);
        if (this.options.popup.enable && this.options.search.enable) {
            this.searchEl = createEl('div', { class: `${prefix}select-search` });
            this.resultEl = createEl('div', { [alias]: 'result' }, this.options.lang.search.start);
            if (!this.options.manual) {
                this.keysEl = createEl('ax-input', { tools: true, placeholder: this.options.lang.search.placeholder, [alias]: 'keys' });
                this.searchEl.appendChild(this.keysEl);
            }
            this.options.search.result && this.searchEl.appendChild(this.resultEl);
            this.wrapEl.insertAdjacentElement('afterbegin', this.searchEl);
        }
        if (this.options.popup.enable && this.options.status) {
            this.checkEl = createEl('ax-checkbox', { label: this.options.lang.check.all, [alias]: 'check' });
            !this.options.multiple && this.checkEl.toggleAttribute('disabled', true);
            this.statsEl = createEl('span', { [alias]: 'stats' });
            this.statusEl = createEl('div', { class: `${prefix}select-status` });
            this.statusEl.append(this.checkEl, this.statsEl);
            this.wrapEl.appendChild(this.statusEl);
            this.checkEl.on('check', (data) => {
                if (data.value === 'ed') {
                    this.treeIns.checkAll();
                    this.checkEl.setAttribute('label', this.options.lang.check.none);
                }
                else if (data.value === 'ing') {
                    this.checkEl.setAttribute('label', this.options.lang.check.ing);
                }
                else {
                    this.treeIns.uncheckAll();
                    this.checkEl.setAttribute('label', this.options.lang.check.ed);
                }
            });
            this.statusHolderEl = createEl('div', { class: `${prefix}select-holder` });
            this.wrapEl.appendChild(this.statusHolderEl);
        }
    }
    updateResult(value, keys) {
        if (!this.options.popup.enable || !this.options.search.enable)
            return;
        this.resultEl.innerHTML = renderTpl(value ? this.options.lang.search.succ : (keys.length ? this.options.lang.search.fail : this.options.lang.search.start), { value, keys: keys.toString() });
    }
    updateStatus() {
        if (!this.options.popup.enable || !this.options.status)
            return;
        let total = this.treeIns.flatData.length, value = this.treeIns.getCheckeds().length;
        this.statsEl.innerHTML = renderTpl(this.options.lang.stats, { value, total });
        this.checkEl.setAttribute('check', !value ? '' : (total === value ? 'ed' : 'ing'));
    }
    setPopup() {
        let footer = this.options.multiple && !this.options.manual ? {
            children: [
                {
                    name: 'clear',
                    action: (resp) => {
                        resp.el.onclick = () => {
                            this.treeIns.clearVals();
                        };
                    },
                },
                'confirm',
                
            ]
        } : false, targetObj = {
            trigger: 'click',
            content: this.wrapEl,
            contType: 'node',
            placement: 'bottom-start',
            padding: false,
            arrow: false,
            tools: false,
            footer,
            canClick: (el) => {
                let arr = [];
                if (this.options.manual) {
                    arr = [this.arrowEl, this.targetEl, this.tagsEl];
                }
                else {
                    let dft = [this.arrowEl, this.targetEl, this.tagsEl, this.tagsEl.emptyEl], tmp = this.tagsIns.data.map((k) => [k.wralEl, k.labelEl, k.iconEl, k.diskEl, k.cubeEl, k.imageEl]).flat().filter(Boolean);
                    arr = !this.options.multiple ? [...tmp, ...dft] : dft;
                }
                return arr.includes(el) ? true : false;
            },
            onInitiated: () => {
                this.popupIns.mainEl.classList.add(`${prefix}select-popup`);
                if (this.options.autoWidth && !(this.popupIns.options.adaptive && getClientType() === 'phone')) {
                    this.popupIns.positionIns.on('changed', () => {
                        this.popupIns.mainEl.style.width = this.popupIns.positionIns.targetData.width + 'px';
                    });
                }
            }
        }, params = extend({ target: targetObj, source: this.options.popup });
        this.popupIns && this.popupIns.destroy();
        this.popupIns = new Popup(this.targetEl, params, false);
    }
    setTags() {
        let content = this.options.popup.enable ?
            { content: '' } :
            {
                content: this.rawHmtl || this.options.content,
                contType: this.options.contType,
                contData: this.options.contData,
                ajx: this.options.ajax,
            }, opts = {
            ...content,
            field: this.options.field,
            min: this.options.multiple ? this.options.min : 0,
            max: this.options.multiple ? this.options.max : 1,
            sliced: this.options.sliced,
            compact: true,
            size: this.options.size,
            unique: { enable: this.options.manual ? this.options.unique : true },
            editor: !this.options.manual || this.options.readonly ? false : true,
            removable: this.options.readonly || !this.options.removable ? false : true,
            empty: { enable: !this.options.manual ? true : false, content: this.options.lang.placeholder },
            onRemoved: (items) => {
                for (let k of items) {
                    this.treeIns.check(k[this.options.field], false);
                }
            },
            onOutput: (data) => {
                super.updateCache({ value: data.value });
                this.output = data;
                super.listen({ name: 'output', params: [this.output] });
            }
        };
        if (this.options.manual) {
            opts.b4Add = (data) => {
                return new Promise(async (resolve, reject) => {
                    let rest = [];
                    for (let k of data) {
                        let tmp = this.treeIns.flatData.find((i) => k[this.options.field] === i[this.options.field]);
                        if (tmp) {
                            if (tmp.checked) {
                                rest.push(k);
                            }
                            else {
                                await this.treeIns.check(tmp, true);
                            }
                        }
                        else {
                            rest.push(k);
                        }
                    }
                    resolve(rest);
                });
            };
            opts.onAdded = () => {
                this.tagsIns.editEl.focus();
            };
        }
        this.tagsIns && this.tagsIns.initiated && this.tagsIns.destroy();
        this.tagsIns = new Tags(this.tagsEl, extend({
            target: opts,
            source: this.options.tags,
        }), false);
    }
    setTree() {
        this.listEl.innerHTML = this.rawHtml;
        let targetObj = {
            content: this.options.content,
            contType: this.options.contType,
            contData: this.options.contData,
            ajax: this.options.ajax,
            passive: this.options.disabled,
            search: {
                target: this.options.search.enable ? (this.options.manual ? this.tagsIns.editEl : this.keysEl) : null,
                trigger: this.options.manual ? 'input' : 'changed',
                ignore: this.options.search.ignore,
                fuzzy: this.options.search.fuzzy,
                value: this.options.search.value,
            },
            disable: this.options.exclude,
            check: {
                type: this.options.multiple ? 'checkbox' : 'radio',
                value: this.options.value,
                sliced: this.options.sliced,
            },
            feature: {
                type: this.options.type,
                check: {
                    linkage: false,
                    span: this.options.span,
                    only: this.options.multiple ? false : true,
                },
                expand: {
                    all: !this.options.collapse,
                }
            },
            output: {
                field: this.options.field,
            },
            onInitiated: () => {
                this.updateStatus();
            },
            onChecked: (items) => {
                if (!this.options.manual)
                    return;
                this.tagsIns.add(items.map((k) => k[this.options.field]));
                if (this.tagsIns.editEl.value) {
                    this.treeIns.search();
                    this.tagsIns.editEl.value = '';
                }
            },
            onUnchecked: (items) => {
                if (!this.options.manual)
                    return;
                this.tagsIns.remove(items.map((k) => k[this.options.field]));
            },
            onCheckedAll: () => {
                if (!this.options.manual)
                    return;
                let tmp = this.treeIns.getCheckeds();
                this.tagsIns.add(filterPrims(tmp));
            },
            onOutput: (data) => {
                if (!this.options.manual) {
                    let vals = filterPrims(data.items);
                    this.tagsIns.updateCont(vals);
                    this.popupIns && this.popupIns.state === 'shown' && !this.options.multiple && this.popupIns.hide();
                    this.updateStatus();
                }
                fieldTools.setVals({
                    target: this.inputEl,
                    value: data.value.split(this.treeIns.options.output.separator),
                    separator: this.treeIns.options.output.separator
                });
            },
            onSearched: (searchs, keys) => {
                this.options.manual && this.popupIns && this.popupIns.show();
                this.updateResult(searchs.length, keys);
            },
        }, params = extend({ target: targetObj, source: this.options.tree, });
        this.treeIns && this.treeIns.initiated && this.treeIns.destroy();
        this.treeIns = new Tree(this.listEl, params, false);
    }
    correctOpts() {
        if (this.inputEl.nodeName === 'SELECT') {
            this.options.manual = false;
            this.options.multiple = this.inputEl.multiple;
            this.options.span = 'leaf';
        }
        this.inputEl.disabled && (this.options.disabled = true);
        this.options.autoWidth && (this.options.popup.size = '');
        this.options.manual === false && (this.options.popup.enable = true);
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}select`);
        this.targetEl.setAttribute('tabindex', 0);
        this.options.classes && classes(this.targetEl).add(this.options.classes);
        this.targetEl.toggleAttribute('inert', this.options.disabled);
        this.inputEl.disabled = this.options.disabled;
        this.targetEl.setAttribute('size', this.options.size);
        this.targetEl.toggleAttribute('readonly', this.options.readonly);
        this.targetEl.toggleAttribute('full', this.options.full);
        this.targetEl.toggleAttribute('manual', this.options.manual);
        this.targetEl.toggleAttribute('multiple', this.options.multiple);
        this.options.maxHeight ? addStyle(this.listEl, 'max-height', this.options.maxHeight) : removeStyle(this.listEl, 'max-height');
    }
    renderFinish() {
    }
    getVals(cb) {
        if (this.destroyed)
            return this;
        return this.tagsIns.getVals((data) => {
            super.listen({ name: 'got', cb, params: [data] });
        });
    }
    async select(vals, cb) {
        if (this.destroyed)
            return this;
        let resp;
        if (this.options.manual) {
            await this.tagsIns.add(vals, (data) => {
                resp = data;
            });
        }
        else {
            await this.treeIns.check(vals, true, (data) => {
                resp = data;
            });
        }
        super.listen({ name: 'selected', cb, params: [resp] });
        return this;
    }
    async deselect(vals, cb) {
        if (this.destroyed || !this.tagsIns.data.length)
            return this;
        await this.tagsIns.remove(vals, (data) => {
            super.listen({ name: 'deselected', cb, params: [data] });
        });
        return this;
    }
    clear(cb) {
        if (this.destroyed)
            return this;
        this.treeIns.uncheckAll();
        this.options.manual && this.tagsIns.clear();
        super.listen({ name: 'cleared', cb });
        return this;
    }
    selectAll(cb) {
        if (this.destroyed)
            return this;
        this.treeIns.checkAll();
        super.listen({ name: 'selectedAll', cb });
        return this;
    }
    disable(cb) {
        this.treeIns.lock();
        this.targetEl.toggleAttribute('inert', true);
        this.inputEl.disabled = true;
        super.listen({ name: 'disabled', cb });
        return this;
    }
    enable(cb) {
        this.treeIns.unlock();
        this.targetEl.toggleAttribute('inert', false);
        this.inputEl.disabled = false;
        super.listen({ name: 'enabled', cb });
        return this;
    }
    destroy(cb) {
        if (this.destroyed)
            return this;
        if (this.options.popup.enable && !this.options.readonly) {
            this.popupIns.destroy();
        }
        this.treeIns.destroy();
        this.tagsIns.destroy();
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

let AXTMP_separator = config.splitHyphen;
const optUpload = [
    {
        attr: 'name',
        prop: 'name',
        value: '',
    },
    {
        attr: 'value',
        prop: 'value',
        value: '',
    },
    {
        attr: 'url',
        prop: 'url',
        value: '',
    },
    {
        attr: 'content',
        prop: 'content',
        value: '',
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'disabled',
        prop: 'disabled',
        value: false,
    },
    {
        attr: 'readonly',
        prop: 'readonly',
        value: false,
    },
    {
        attr: 'multiple',
        prop: 'multiple',
        value: true,
    },
    {
        attr: 'manual',
        prop: 'manual',
        value: false,
    },
    {
        attr: 'type',
        prop: 'type',
        value: 'text',
    },
    {
        attr: 'icons',
        prop: 'icons',
        value: {},
    },
    {
        attr: 'type-getter',
        prop: 'typeGetter',
        value: null,
    },
    {
        attr: 'feature',
        prop: 'feature',
        value: '',
    },
    {
        attr: 'limit',
        prop: 'limit',
        value: {
            min: 0,
            max: 0,
            size: 0,
            suffix: '',
        },
    },
    {
        attr: 'accept',
        prop: 'accept',
        value: '',
    },
    {
        attr: 'size',
        prop: 'size',
        value: 'md',
    },
    {
        attr: 'table',
        prop: 'table',
        value: {
            header: true,
        },
    },
    {
        attr: 'pastable',
        prop: 'pastable',
        value: true,
    },
    {
        attr: 'choose-btn',
        prop: 'chooseBtn',
        value: {
            target: '',
            icon: `${prefix}icon-plus`,
            attrs: {},
        },
    },
    {
        attr: 'upload-btn',
        prop: 'uploadBtn',
        value: {
            enable: false,
            target: '',
            icon: `${prefix}icon-upload`,
            attrs: {},
        },
    },
    {
        attr: 'clear-btn',
        prop: 'clearBtn',
        value: {
            enable: false,
            target: '',
            icon: `${prefix}icon-trash`,
            attrs: {},
        },
    },
    {
        attr: 'picture-btn',
        prop: 'pictureBtn',
        value: {
            icon: `${prefix}icon-plus`,
            attrs: {},
        },
    },
    {
        attr: 'gallery-btn',
        prop: 'galleryBtn',
        value: {
            icon: `${prefix}icon-addpic`,
            attrs: {},
        },
    },
    {
        attr: 'separator',
        prop: 'separator',
        value: AXTMP_separator,
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'status',
        prop: 'status',
        value: '',
    },
    {
        attr: 'success',
        prop: 'success',
        value: (data) => data.code == 200,
    },
    {
        attr: 'cloud',
        prop: 'cloud',
        value: {
            enable: false,
            field: '',
            server: '',
            domain: '',
            map: { name: '', url: '', size: '', time: '' }
        },
    },
    {
        attr: 'b4-clear',
        prop: 'b4Clear',
        value: null,
    },
    {
        attr: 'b4-remove',
        prop: 'b4Remove',
        value: null,
    },
    {
        attr: 'b4-upload',
        prop: 'b4Upload',
        value: null,
    },
    {
        attr: 'b4-uploadall',
        prop: 'b4UploadAll',
        value: null,
    },
    {
        attr: 'on-output',
        prop: 'onOutput',
        value: null,
    }, {
        attr: 'on-rendered',
        prop: 'onRendered',
        value: null,
    },
    {
        attr: 'on-added',
        prop: 'onAdded',
        value: null
    },
    {
        attr: 'on-removed',
        prop: 'onRemoved',
        value: null
    },
    {
        attr: 'on-cleared',
        prop: 'onCleared',
        value: null
    },
    {
        attr: 'on-pasted',
        prop: 'onPasted',
        value: null
    },
    {
        attr: 'on-dropped',
        prop: 'onDropped',
        value: null
    },
    {
        attr: 'on-uploading',
        prop: 'onUploading',
        value: null
    },
    {
        attr: 'on-uploaded',
        prop: 'onUploaded',
        value: null
    },
    {
        attr: 'on-received',
        prop: 'onReceived',
        value: null
    },
    {
        attr: 'on-receivedAll',
        prop: 'onReceivedAll',
        value: null
    },
    {
        attr: 'on-uploadAll',
        prop: 'onUploadAll',
        value: null
    },
    {
        attr: 'on-uploadedAll',
        prop: 'onUploadedAll',
        value: null
    }, {
        attr: 'on-failed',
        prop: 'onFailed',
        value: null
    },
    {
        attr: 'on-error',
        prop: 'onError',
        value: null
    },
    {
        attr: 'on-updatedcont',
        prop: 'onUpdatedCont',
        value: null
    },
    {
        attr: 'on-request',
        prop: 'onRequest',
        value: null
    },
    {
        attr: 'on-locked',
        prop: 'onLocked',
        value: null
    },
    {
        attr: 'on-unlocked',
        prop: 'onUnlocked',
        value: null
    },
    ...optBase
];

class Upload extends ModBaseListenCache {
    options = {};
    flatData;
    treeData;
    dftTypes;
    fileChangeEvt;
    chooseEvt;
    clearEvt;
    uploadEvt;
    dragLeaveEvt;
    dragOverEvt;
    dragEnterEvt;
    dropEvt;
    tipsEl;
    summaryEl;
    infoEl;
    listEl;
    tableEl;
    theadEl;
    fileEl;
    inputEl;
    footEl;
    headEl;
    chooseBtn;
    uploadBtn;
    clearBtn;
    ctrlvEl;
    tipsText;
    name;
    singleSizeLimit;
    fileTypes;
    passedItems;
    allItems;
    output;
    files;
    value;
    validItems;
    static hostType = 'node';
    static optMaps = optUpload;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            host: elem,
            maps: Upload.optMaps,
            component: true,
            spread: ['cloud', 'uploadBtn', 'clearBtn']
        });
        
        
        
        
        
        
        
        
        
        
        
        
        
        this.dftTypes = {
            'image': `${prefix}icon-image`,
            'video': `${prefix}icon-video`,
            'audio': `${prefix}icon-music`,
            'file': `${prefix}icon-file-text`,
        };
        let _this = this;
        this.fileChangeEvt = function () {
            let files = [...this.files];
            _this.renderList(files);
            this.value = '';
            _this.listen({ name: 'changed', params: [files] });
        };
        this.chooseEvt = () => {
            let globalValid = this.globalValid();
            if (!globalValid.passed) {
                new Message({
                    content: globalValid.msg,
                    status: 'error',
                }).show();
                return;
            }
            this.fileEl.click();
        };
        this.clearEvt = () => {
            this.clear.call(this);
        };
        this.uploadEvt = () => {
            this.uploadAll();
        };
        this.dragLeaveEvt = (e) => {
            e.preventDefault();
            this.targetEl.removeAttribute('dragging');
        };
        this.dragOverEvt = (e) => {
            e.preventDefault();
            this.targetEl.toggleAttribute('dragging', true);
        };
        this.dragEnterEvt = (e) => {
            e.preventDefault();
        };
        this.dropEvt = async (e) => {
            e.preventDefault();
            this.targetEl.removeAttribute('dragging');
            let items = e.dataTransfer.items;
            for (let k of items) {
                if (k.kind === "file") {
                    let entry = k.webkitGetAsEntry();
                    this.getEntries(entry);
                }
            }
            super.listen({ name: 'dropped', params: [items] });
        };
        this.tipsEl = createEl('div', { [alias]: 'tips' });
        this.summaryEl = createEl('div', { [alias]: 'summary' });
        this.infoEl = createEl('div', { class: `${prefix}upload-info` });
        this.createSetList();
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    createSetList() {
        let classes = prefix + 'reset';
        if (this.options.type === 'card') {
            classes += ` ${prefix}grid ${prefix}g-xs`;
            this.options.columns && (classes += ` ${prefix}grid-${this.options.columns}`);
        }
        this.listEl = createEl(this.options.type === 'table' ? 'table' : 'ul', { class: `${this.options.type !== 'table' ? classes : ''} ${prefix}upload-list` });
        if (this.options.type === 'table') {
            this.tableEl = createEl('table');
            this.listEl = createEl('tbody', { class: `${prefix}upload-list` });
            if (this.options.table.header) {
                this.theadEl = createEl('thead', {}, this.options.lang.thead.map((k) => `<th>${k}</th>`).join(''));
                this.tableEl.appendChild(this.theadEl);
            }
            this.tableEl.appendChild(this.listEl);
        }
        else {
            this.listEl = createEl('ul', { class: `${classes} ${prefix}upload-list` });
        }
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch (err) {
            err ? console.error(err) : console.warn(config.warn.init);
            return this;
        }
        this.passedItems = [];
        this.allItems = [];
        this.output = { value: '', raw: '', items: [] };
        this.singleSizeLimit = this.options.limit.size * 1024 * 1024;
        this.setFeature();
        this.getFileTypes();
        this.name = this.targetEl.name || this.options.name;
        this.createSkeleton();
        this.setAttrs();
        this.renderFinish();
        this.initShow();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    initShow() {
        if (!isEmpty(this.options.value)) {
            this.add(this.options.value);
        }
        else {
            let tmp = this.rawHtml.trim();
            tmp && this.add(tmp);
        }
    }
    setFeature() {
        if (this.options.feature === 'picture') {
            this.options.type = 'cube';
        }
    }
    getFileTypes() {
        this.fileTypes = { ...this.dftTypes, ...this.options.icons };
    }
    getTips() {
        this.tipsText = [this.options.lang.tips.free];
        this.options.limit.suffix && this.tipsText.push(renderTpl(this.options.lang.tips.suffix, { value: this.options.limit.suffix }));
        this.options.limit.size && this.tipsText.push(renderTpl(this.options.lang.tips.size, { value: this.options.limit.size }));
        this.options.limit.min && this.tipsText.push(renderTpl(this.options.lang.tips.min, { value: this.options.limit.min }));
        this.options.limit.max && this.tipsText.push(renderTpl(this.options.lang.tips.max, { value: this.options.limit.max }));
        return this.tipsText.join(',');
    }
    createStart() {
        if (this.options.feature === 'gallery') {
            this.chooseBtn = this.createBox(this.options.feature, 'div');
        }
        else if (this.options.feature === 'picture') {
            this.chooseBtn = this.createBox(this.options.feature, 'li');
        }
        else {
            this.chooseBtn = getEl(this.options.chooseBtn.target) || this.createBtn('choose');
        }
    }
    createBox(type, node = 'li') {
        let iconStr = this.options[type + 'Btn'].icon ? `<i ${alias}="icon" class="${this.options[type + 'Btn'].icon}"></i>` : '', textStr = this.options.lang.button[type] ? `<div ${alias}="text">${this.options.lang.button[type]}</div>` : '';
        return createEl(node, Object.assign({ [alias]: ['picture', 'gallery'].includes(type) ? 'choose' : type }, type !== 'gallery' ? { tabindex: 0 } : {}, this.options[type + 'Btn'].attrs), `${iconStr}${textStr}`);
    }
    createBtn(type) {
        return createEl('ax-btn', Object.assign({ [alias]: type, size: this.options.size, tabindex: 0, icon: this.options[type + 'Btn'].icon, label: this.options.lang.button[type] }, this.options[type + 'Btn'].attrs));
    }
    createInput(hasText = true, hasFile = true) {
        if (hasText) {
            this.inputEl = createEl('input', { type: 'hidden', readonly: '', name: this.name });
        }
        if (hasFile) {
            this.fileEl = createEl('input', { type: 'file', readonly: '' });
        }
    }
    createSkeleton() {
        if (this.targetEl) {
            if (this.targetEl.nodeName === 'INPUT') {
                let tmp = this.targetEl;
                if (tmp.type === 'file') {
                    this.fileEl = tmp;
                    this.fileEl.readOnly = true;
                    this.createInput(true, false);
                }
                else {
                    this.inputEl = tmp;
                    this.inputEl.type = 'hidden';
                    this.inputEl.name = this.name;
                    this.inputEl.readOnly = true;
                    this.createInput(false, true);
                }
                this.targetEl = createEl('div');
                tmp.insertAdjacentElement('beforeBegin', this.targetEl);
            }
            else {
                this.createInput(true, true);
            }
        }
        else {
            this.targetEl = createEl('div');
            this.createInput(true, true);
        }
        this.inputEl.toggleAttribute(ax.embedSign, true);
        this.fileEl.toggleAttribute(ax.embedSign, true);
        this.targetEl.innerHTML = this.listEl.innerHTML = '';
        this.options.accept && this.fileEl.setAttribute('accept', this.options.accept);
        if (this.fileEl.multiple) {
            this.options.multiple = true;
        }
        else {
            this.options.multiple && (this.fileEl.toggleAttribute('multiple', true));
        }
        !this.fileEl.multiple && (this.options.limit.min = 0, this.options.limit.max = 1);
        this.targetEl.append(this.inputEl, this.fileEl);
        this.targetEl.appendChild(this.options.type === 'table' ? this.tableEl : this.listEl);
        this.footEl = createEl('div', { class: `${prefix}upload-foot` }, this.infoEl);
        if (this.options.status) {
            this.targetEl.setAttribute('status', this.options.status);
            if (this.options.status.includes('tips')) {
                this.tipsEl.innerHTML = this.getTips();
                this.infoEl.appendChild(this.tipsEl);
            }
            if (this.options.status.includes('summary')) {
                this.summaryEl.innerHTML = this.getSummary();
                this.infoEl.appendChild(this.summaryEl);
            }
        }
        if (this.options.feature === 'gallery') {
            this.createCtrlv();
            this.targetEl.appendChild(this.footEl);
            this.chooseBtn = this.createBox(this.options.feature, 'div');
            this.targetEl.insertAdjacentElement('afterBegin', this.chooseBtn);
            this.targetEl.setAttribute('tabindex', 0);
            if (this.options.clearBtn.enable) {
                this.clearBtn = this.createBtn('clear');
                this.footEl.appendChild(this.clearBtn);
            }
            if (this.options.uploadBtn.enable) {
                this.uploadBtn = this.createBtn('upload');
                this.footEl.appendChild(this.uploadBtn);
            }
        }
        else if (this.options.feature === 'picture') {
            this.targetEl.appendChild(this.footEl);
            this.chooseBtn = this.createBox(this.options.feature, 'li');
            this.listEl.appendChild(this.chooseBtn);
            if (this.options.clearBtn.enable) {
                this.clearBtn = this.createBox('clear', 'li');
                this.chooseBtn.insertAdjacentElement('afterEnd', this.clearBtn);
            }
            if (this.options.uploadBtn.enable) {
                this.uploadBtn = this.createBox('upload', 'li');
                this.chooseBtn.insertAdjacentElement('afterEnd', this.uploadBtn);
            }
        }
        else {
            this.headEl = createEl('div', { class: `${prefix}upload-head` });
            this.listEl.insertAdjacentElement('beforeBegin', this.footEl);
            let btn = getEl(this.options.chooseBtn.target);
            if (btn) {
                this.chooseBtn = btn;
            }
            else {
                this.chooseBtn = this.createBtn('choose');
                this.headEl.appendChild(this.chooseBtn);
            }
            if (this.options.uploadBtn.enable) {
                let tmp = getEl(this.options.uploadBtn.target);
                if (tmp) {
                    this.uploadBtn = tmp;
                }
                else {
                    this.uploadBtn = this.createBtn('upload');
                    this.headEl.appendChild(this.uploadBtn);
                }
            }
            if (this.options.clearBtn.enable) {
                let tmp = getEl(this.options.clearBtn.target);
                if (tmp) {
                    this.clearBtn = tmp;
                }
                else {
                    this.clearBtn = this.createBtn('clear');
                    this.headEl.appendChild(this.clearBtn);
                }
            }
            this.targetEl.insertAdjacentElement('afterBegin', this.headEl);
        }
    }
    addEvts() {
        this.chooseBtn.addEventListener('click', this.chooseEvt, false);
        this.uploadBtn && this.uploadBtn.addEventListener('click', this.uploadEvt, false);
        this.clearBtn && this.clearBtn.addEventListener('click', this.clearEvt, false);
        this.fileEl.addEventListener('change', this.fileChangeEvt, false);
        this.targetEl.addEventListener("dragleave", this.dragLeaveEvt, false);
        this.targetEl.addEventListener("dragover", this.dragOverEvt, false);
        this.targetEl.addEventListener("dragenter", this.dragEnterEvt, false);
        this.targetEl.addEventListener("drop", this.dropEvt, false);
    }
    removeEvts() {
        this.chooseBtn.removeEventListener('click', this.chooseEvt);
        this.uploadBtn && this.uploadBtn.removeEventListener('click', this.uploadEvt);
        this.clearBtn && this.clearBtn.removeEventListener('click', this.clearEvt);
        this.fileEl.removeEventListener('change', this.fileChangeEvt);
        this.targetEl.removeEventListener("dragleave", this.dragLeaveEvt);
        this.targetEl.removeEventListener("dragover", this.dragOverEvt);
        this.targetEl.removeEventListener("dragenter", this.dragEnterEvt);
        this.targetEl.removeEventListener("drop", this.dropEvt);
    }
    renderFinish() {
        this.removeEvts();
        this.addEvts();
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}upload`);
        this.options.type ? this.targetEl.setAttribute('type', this.options.type) : this.targetEl.removeAttribute('type');
        this.targetEl.toggleAttribute('pastable', this.options.pastable);
        this.targetEl.toggleAttribute('inert', this.options.disabled);
        this.options.feature ? this.targetEl.setAttribute('feature', this.options.feature) : this.targetEl.removeAttribute('feature');
        this.targetEl.setAttribute('size', this.options.size);
        this.updateInsProg('ready');
    }
    createCtrlv() {
        if (this.options.pastable) {
            this.ctrlvEl = createEl('i', { [alias]: 'ctrlv' }, this.options.lang.paste.before);
            this.targetEl.appendChild(this.ctrlvEl);
            this.ctrlvEl.onclick = () => {
                this.ctrlvEl.innerHTML = this.options.lang.paste.ing;
                this.ctrlvEl.toggleAttribute('contenteditable', true);
                this.ctrlvEl.focus();
            };
            this.ctrlvEl.onpaste = (e) => {
                e.preventDefault();
                this.ctrlvEl.toggleAttribute('contenteditable', false);
                let data = e.clipboardData;
                if (data) {
                    let item = data.items[0];
                    if (item && item.kind === 'file' && item.type.includes('image')) {
                        this.renderList([item.getAsFile()]);
                        this.ctrlvEl && (this.ctrlvEl.innerHTML = this.options.lang.paste.after);
                    }
                    super.listen({ name: 'pasted', params: [item] });
                }
            };
            this.targetEl.onmouseleave = () => {
                this.ctrlvEl.innerHTML = this.options.lang.paste.before;
            };
        }
    }
    getSummary() {
        let originalSize = 0, realSize, totalNum = this.passedItems.length, receivedItems = this.passedItems.filter((i) => i.progress == 'received'), okNum = receivedItems.length;
        for (let k of receivedItems) {
            originalSize += k.file.size;
        }
        realSize = convertByte({ val: originalSize }).str;
        return renderTpl(this.options.lang.summary, { total: totalNum, count: okNum, size: realSize });
    }
    createItem(item) {
        let name = item.file.name, size = item.file.size, passed = item.valid.passed, msg = item.valid.msg, tpl = '', part1 = `
        <div class="${prefix}upload-mask">
            ${this.options.manual ? '<i ' + alias + '="upload" class="' + prefix + 'icon-upload"></i>' : ''}
            <i ${alias}="view" class="${prefix}icon-zoomin"></i>
            <a ${alias}="download" href="###" class="${prefix}icon-download"></a>
            <i ${alias}="remove" class="${prefix}icon-trash"></i>
        </div>
        <ax-progress ${alias}="progress" thk="xs" size="sm" type="circle"></ax-progress>
        <i ${alias}="state"></i>
        <i ${alias}="sign" class="${prefix}icon-check"></i>
        <div ${alias}="message">${passed ? msg : ''}</div>
        `, part2 = `
                <div class="${prefix}upload-info">
                    <a href="###" ${alias}="title">${name}</a>
                    ${this.options.type === 'bullet' ? '<div ' + alias + '="size">' + convertByte({ val: size }).str + '</div>' : ''}
                </div>
                <ax-progress ${alias}="progress" label="false" thk="xs"></ax-progress>
                <i ${alias}="message">${passed ? msg : ''}</i>
                <i ${alias}="percent"></i>
                <i ${alias}="state"></i>
                <i ${alias}="sign" class="${prefix}icon-check"></i>
                ${this.options.manual ? '<i ' + alias + '="upload" class="' + prefix + 'icon-upload"></i>' : ''}
                <i ${alias}="remove" class="${prefix}icon-trash"></i>
                `;
        if (this.options.type === 'card') {
            tpl = `
                    <li valid=${passed ? 'success' : 'failed'}>
                        <div class="${prefix}upload-file">
                            ${part1}
                        </div>
                        <div class="${prefix}upload-info">
                            <div ${alias}="title">${name}</div>
                            <div ${alias}="size">${convertByte({ val: size }).str}</div>
                            <div ${alias}="url"></div>
                        </div>
                    </li>
                    `;
        }
        else if (this.options.type === 'cube') {
            tpl = `
                    <li valid=${passed ? 'success' : 'failed'}>
                        <div class="${prefix}upload-file"></div>
                        ${part1}
                    </li>
                    `;
        }
        else if (this.options.type == 'bullet') {
            tpl = `
                    <li valid=${passed ? 'success' : 'failed'}>
                        <a href="###" class="${prefix}upload-file"></a>
                        ${part2}
                    </li>
                    `;
        }
        else if (this.options.type === 'table') {
            let btns = `
                ${this.options.manual ? '<i ' + alias + '="upload" class="' + prefix + 'icon-upload"></i>' : ''}
                <i ${alias}="remove" class="${prefix}icon-trash"></i>
            `;
            tpl = `
                    <tr valid=${passed ? 'success' : 'failed'}>
                        <td><div class="${prefix}upload-file"></div></td>
                        <td><div class="${prefix}upload-info"><a href="###" ${alias}="title">${name}</a></div></td>
                        <td>${convertByte({ val: size }).str}</td>
                        <td><ax-progress ${alias}="progress" thk="sm"></ax-progress></td>
                        <td><i ${alias}="message">${passed ? msg : ''}</i></td>
                        <td><span ${alias}="state">${passed ? this.options.lang.progress.passed : this.options.lang.progress.notPassed}</span></td>
                        <td>${btns}</td>
                    </tr>
                    `;
        }
        else {
            tpl = `
                    <li valid=${passed ? 'success' : 'failed'}>
                        <i ${alias}="icon" class="${prefix}icon-attach"></i>
                        ${part2}
                    </li>
                    `;
        }
        item.wrapEl = tplToEl(tpl);
        item.fileEl = item.wrapEl.querySelector(`.${prefix}upload-file`);
        item.fileEl && this.fillHeader(item);
        if (this.options.feature === 'picture') {
            this.chooseBtn.insertAdjacentElement('beforeBegin', item.wrapEl);
        }
        else {
            this.listEl.appendChild(item.wrapEl);
        }
    }
    updateInsProg(prog) {
        this.targetEl.setAttribute('progress', prog);
    }
    updateItemProg(item, prog) {
        item.progress = prog;
        item.wrapEl.setAttribute('progress', prog);
        item.stateEl && (item.stateEl.innerHTML = this.options.lang.progress[prog]);
    }
    updateItemValid(item, valid) {
        item.wrapEl.setAttribute('valid', valid);
    }
    renderItem(file) {
        let fileValid = this.fileValid(file), item = { file, valid: fileValid };
        this.createItem(item);
        item.uploadEl = item.wrapEl.querySelector(`[${alias}="upload"]`);
        item.removeEl = item.wrapEl.querySelector(`[${alias}="remove"]`);
        item.stateEl = item.wrapEl.querySelector(`[${alias}="state"]`);
        item.msgEl = item.wrapEl.querySelector(`[${alias}="message"]`);
        item.progEl = item.wrapEl.querySelector(`ax-progress`);
        if (item.uploadEl) {
            item.uploadEl.onclick = async () => {
                try {
                    this.options.b4Upload && await this.options.b4Upload.call(this, item);
                }
                catch (err) {
                    err ? console.error(err) : console.warn('The "upload" operation has been blocked!');
                    return this;
                }
                this.upload(item);
            };
        }
        if (item.removeEl) {
            item.removeEl.onclick = async () => {
                try {
                    this.options.b4Remove && await this.options.b4Remove.call(this, item);
                }
                catch (err) {
                    err ? console.error(err) : console.warn('The "remove" operation has been blocked!');
                    return this;
                }
                this.remove(item);
            };
        }
        this.allItems.push(item);
        fileValid.passed && this.passedItems.push(item);
        this.updateItemProg(item, 'rendered');
        super.listen({ name: 'rendered', params: [item] });
        return item;
    }
    getEntries(entry) {
        if (entry.isFile) {
            entry.file((i) => this.renderList([i]));
        }
        else {
            entry.createReader().readEntries((k) => k.forEach((i) => this.getEntries(i)));
        }
    }
    hasSame(file) {
        return this.allItems.length ? this.allItems.some((i) => (i.file.name === file.name && i.file.size === file.size)) : false;
    }
    renderList(files) {
        if (!files.length)
            return;
        for (let k of files) {
            !this.hasSame(k) && this.renderItem(k);
        }
        !this.options.manual && this.uploadAll();
        this.updateInsProg('processing');
    }
    async uploadAll(cb) {
        if (!this.options.url)
            throw new Error('The parameter "url" (the address of the page to send data) is required!');
        let globalValid = this.globalValid();
        if (!globalValid.passed) {
            new Message({
                content: globalValid.msg,
                status: 'error',
            }).show();
            return this;
        }
        else {
            if (this.allItems.length < this.options.limit.min) {
                new Message({
                    content: globalValid.msg,
                    status: 'warn',
                }).show();
            }
        }
        let items = this.getUploadingItems();
        try {
            this.options.b4UploadAll && await this.options.b4UploadAll.call(this, items);
        }
        catch (err) {
            err ? console.error(err) : console.warn('The "upload all files" operation has been blocked!');
            return this;
        }
        super.listen({ name: 'uploadAll', cb, params: [items] });
        this.saveRaw();
        await Promise.all(items.map((k) => this.upload(k, true)));
        this.setInputVal();
        this.listenReceivedAll();
        return this;
    }
    getUploadingItems() {
        return this.passedItems.filter((i) => this.canUpload(i));
    }
    actOpt(item) {
        let file = item.file, dom = item.wrapEl, url = item.data.url; item.file.name;
        if (this.options.cloud.enable)
            url = url ? this.options.cloud.domain + url : 'javascript:void(0);';
        let downloadBtn = dom.querySelector(`[${alias}="download"]`), viewBtn = dom.querySelector(`[${alias}="view"]`);
        if (downloadBtn) {
            if (this.options.cloud.enable) {
                downloadBtn.onclick = () => fileTools.download(url, file.name);
            }
            else {
                downloadBtn.setAttribute('download', '');
                downloadBtn.setAttribute('href', url);
            }
        }
        if (viewBtn) {
            this.getFileType(file);
        }
        if (['text', 'bullet', 'table'].includes(this.options.type)) {
            let titleEl = dom.querySelector(`[${alias}="title"]`), fileEl = dom.querySelector(`.${prefix}upload-file`);
            titleEl.setAttribute('href', url);
            titleEl.setAttribute('target', '_blank');
            if (fileEl) {
                fileEl.setAttribute('href', url);
                fileEl.setAttribute('target', '_blank');
            }
        }
    }
    canUpload(item) {
        return item.valid.passed && !['uploading', 'uploaded', 'received'].includes(item.progress);
    }
    async upload(item, eager = false) {
        let data = new FormData(), file = item.file, dom = item.wrapEl, fixEl = (flag, message) => {
            if (flag) {
                this.actOpt(item);
                this.updateItemProg(item, 'received');
            }
            else {
                this.updateItemProg(item, 'failed');
                this.updateItemValid(item, 'failed');
                item.msgEl.innerText = message;
                this.passedItems = this.allItems.filter((i) => i.valid.passed);
            }
            this.options.status.includes('summary') && (this.summaryEl.innerHTML = this.getSummary());
        };
        if (!this.canUpload(item))
            return this;
        try {
            this.options.b4Upload && await this.options.b4Upload.call(this, item);
        }
        catch (err) {
            err ? console.error(err) : console.warn('The "upload" operation has been blocked!');
            return this;
        }
        data.append(this.name || 'file', file);
        if (this.options.cloud.enable) {
            await ajax({
                url: this.options.url,
                before: () => {
                    this.updateItemProg(item, 'getAuth');
                },
                success: (resp) => {
                    for (let k in resp.content)
                        data.append(k, resp.content[k]);
                    this.options.cloud.append && this.options.cloud.append(data, file);
                    this.updateItemProg(item, 'authorized');
                },
                error: () => {
                    this.updateItemProg(item, 'unauthorized');
                    this.updateItemValid(item, 'failed');
                    this.validItems = this.allItems.filter((i) => i.valid.passed);
                    return;
                }
            });
        }
        this.updateItemProg(item, 'uploading');
        !eager && this.saveRaw();
        await ajax({
            url: this.options.cloud.enable ? this.options.cloud.server : this.options.url,
            data,
            uploading: (resp) => {
                item.abort = resp.abort;
                item.progEl.setAttribute('value', resp.percent);
                if (['text', 'bullet'].includes(this.options.type)) {
                    dom.querySelector(`[${alias}="percent"]`).innerHTML = resp.result;
                }
                if (resp.percent >= 100) {
                    this.updateItemProg(item, 'uploaded');
                    super.listen({ name: 'uploaded', params: [item] });
                    if (this.passedItems.every((i) => i.valid.passed && ['uploaded', 'received'].includes(i.progress))) {
                        super.listen({ name: 'uploadedAll' });
                    }
                }
                else {
                    this.updateItemProg(item, 'uploading');
                    super.listen({ name: 'uploading', params: [{ item, content: resp }] });
                }
            },
            success: async (resp) => {
                let name = '', msg = '', url = '', size = 0, time = 0, passed, map = this.options.cloud.map, forceMsg = (item, msg, type) => {
                    if (msg) {
                        item.valid.msg = msg;
                    }
                    else {
                        item.valid.msg = this.options.lang.message.single[type];
                    }
                }, getKeys = (obj, str) => {
                    if (!str)
                        return '';
                    if (!str.includes('.'))
                        return obj[str];
                    let arr = str.split("."), len = arr.length, value = len === 1 ? obj[arr[0]] : len === 2 ? obj[arr[0]][arr[1]] : '';
                    return value;
                };
                if (this.options.cloud.enable) {
                    name = getKeys(resp.content, map.name || 'name');
                    url = getKeys(resp.content, map.url || 'url');
                    size = getKeys(resp.content, map.size || 'size');
                    time = getKeys(resp.content, map.time || 'time');
                    msg = getKeys(resp.content, map.msg || 'msg');
                }
                else {
                    name = resp.content.name || item.file.name;
                    url = resp.content.url;
                    size = file.size;
                    time = resp.content.time;
                    msg = resp.content.msg;
                }
                passed = (this.options.success && this.options.success(resp.content)) || !!url;
                item.resp = resp.content;
                item.data = { name, url, size, time };
                item.valid.passed = passed;
                if (!passed) {
                    forceMsg(item, msg, 'failed');
                    super.listen({ name: 'failed', params: [{ item, content: resp.content }] });
                }
                else {
                    forceMsg(item, msg, 'success');
                    super.listen({ name: 'received', params: [{ item, content: resp.content }] });
                }
                fixEl(passed, item.valid.msg);
            },
            error: (resp) => {
                fixEl(false, this.options.lang.message.single.failed);
                super.listen({ name: 'error', params: [{ item, content: resp.content }] });
            }
        });
        if (!eager) {
            this.setInputVal();
            this.listenReceivedAll();
        }
    }
    listenReceivedAll() {
        if (this.passedItems.every((i) => i.valid.passed && i.progress == 'received')) {
            super.listen({ name: 'receivedAll' });
        }
    }
    items2Vals(vals = this.passedItems) {
        let items = vals.map((k) => k.data).filter(Boolean), arr = [];
        for (let k of items)
            arr.push(k.url);
        return { value: arr.join(this.options.separator), items };
    }
    saveRaw(items = this.passedItems) {
        this.output.raw = this.items2Vals(items).value;
    }
    setInputVal(value = this.passedItems) {
        Object.assign(this.output, this.items2Vals(value));
        if (this.inputEl) {
            this.inputEl.value = this.output.value;
            this.inputEl.onchange && this.inputEl.onchange();
        }
        super.listen({ name: 'output', params: [this.output] });
    }
    getFileType(file) {
        let type = file.type, sfx = fileTools.getSuffix(file).toLowerCase(), tmp = '';
        if (type.startsWith('image')) {
            tmp = 'image';
        }
        else if (type.startsWith('video')) {
            tmp = 'video';
        }
        else if (type.startsWith('audio')) {
            tmp = 'audio';
        }
        else {
            tmp = this.options.typeGetter ? this.options.typeGetter(file, sfx) : 'file';
        }
        return tmp;
    }
    async fillHeader(item) {
        let file = item.file, type = this.getFileType(file), elem;
        if (type === 'image') {
            await fileTools.getBase64(file, (data) => { elem = createEl('img', { src: data }); });
        }
        else if (['video', 'audio'].includes(type)) {
            await fileTools.getBase64(file, (data) => elem = createEl(type, { src: data, controls: '' }));
        }
        else {
            elem = createEl('i', { class: this.fileTypes[type] || this.fileTypes.file, [alias]: 'icon' });
        }
        item.fileEl.appendChild(elem);
    }
    fileValid(file) {
        let msg = this.options.lang.message.single.passed, passed = true, suffix = fileTools.getSuffix(file);
        if (this.allItems.length > this.options.limit.max && this.options.limit.max) {
            msg = this.options.lang.message.single.max;
            passed = false;
        }
        else if (this.singleSizeLimit && (file.size > this.singleSizeLimit)) {
            msg = this.options.lang.message.single.size;
            passed = false;
        }
        else if (this.options.limit.suffix && !this.options.limit.suffix.includes(suffix)) {
            msg = this.options.lang.message.single.suffix;
            passed = false;
        }
        return { passed, msg };
    }
    globalValid() {
        let msg = this.options.lang.message.global.passed, passed = true;
        if (this.allItems.length < this.options.limit.min) {
            msg = renderTpl(this.options.lang.message.global.min, { value: this.options.limit.min });
            passed = true;
        }
        else if (this.options.limit.max && this.allItems.length > this.options.limit.max) {
            msg = renderTpl(this.options.lang.message.global.max, { value: this.options.limit.max });
            passed = false;
        }
        return { passed, msg };
    }
    findItem(data) {
        let type = getDataType(data), result;
        if (type === 'String') {
            result = this.allItems.find((i) => i.file.name === data);
        }
        else if (type === 'Number') {
            result = this.allItems[data];
        }
        else if (type === 'File') {
            result = this.allItems.find((i) => i.file === data);
        }
        else if (type === 'Object' && data.wrapEl) {
            result = this.allItems.find((i) => i === data);
        }
        return result;
    }
    async remove(data, cb) {
        if (this.destroyed)
            return this;
        let item = this.findItem(data);
        if (!item)
            return this;
        try {
            this.options.b4Remove && await this.options.b4Remove.call(this, item);
        }
        catch (err) {
            err ? console.error(err) : console.warn('The "remove" operation has been blocked!');
            return this;
        }
        this.saveRaw();
        item.abort && item.abort();
        item.wrapEl.remove();
        this.allItems = this.allItems.filter((i) => i !== item);
        this.passedItems = this.passedItems.filter((i) => i !== item);
        if (this.passedItems.length) {
            this.setInputVal();
        }
        else {
            this.updateInsProg('ready');
            this.setInputVal();
        }
        this.options.status.includes('summary') && (this.summaryEl.innerHTML = this.getSummary());
        super.listen({ name: 'removed', cb, params: [item] });
        return this;
    }
    async clear(cb) {
        if (this.destroyed)
            return this;
        try {
            this.options.b4Clear && await this.options.b4Clear.call(this);
        }
        catch (err) {
            err ? console.error(err) : console.warn('The "clear" operation has been blocked!');
            return this;
        }
        this.saveRaw();
        for (let k of this.passedItems) {
            k.abort && k.abort();
            k.wrapEl.remove();
        }
        this.passedItems = [];
        this.allItems = [];
        this.setInputVal();
        this.updateInsProg('ready');
        this.options.status.includes('summary') && (this.summaryEl.innerHTML = this.getSummary());
        super.listen({ name: 'cleared', cb });
        return this;
    }
    getVals() {
        if (this.destroyed)
            return this;
        return this.inputEl.value;
    }
    async add(data, cb) {
        if (this.destroyed)
            return this;
        let newData = valToArr(data).map((k) => { return typeof k === 'string' ? { url: k, name: '' } : k; }).filter(Boolean), result = [];
        if (!newData.length)
            return;
        this.updateInsProg('processing');
        this.saveRaw();
        for (let k of newData) {
            try {
                await fileTools.urlToFile(k.url, k.name, (file) => {
                    if (this.hasSame(file))
                        return;
                    let item = this.renderItem(file);
                    item.valid = {
                        passed: true,
                        msg: this.options.lang.message.single.success
                    };
                    item.data = {
                        name: file.name,
                        url: k.url,
                        time: k.time || file.lastModified,
                        size: file.size,
                    };
                    this.updateItemProg(item, 'received');
                    this.actOpt(item);
                    result.push(item);
                });
            }
            catch {
                console.error(`File address error, or the file may be blocked due to cross-origin restrictions:${k.url}`);
                continue;
            }
        }
        this.options.status.includes('summary') && (this.summaryEl.innerHTML = this.getSummary());
        this.setInputVal();
        super.listen({ name: 'added', cb, params: [result] });
        return this;
    }
    
    destroy(cb) {
        if (this.destroyed)
            return this;
        this.removeEvts();
        for (let k of this.passedItems) {
            k.abort && k.abort();
            k.uploadEl && (k.uploadEl.onclick = null);
            k.removeEl && (k.removeEl.onclick = null);
        }
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

const optPanel = [
    {
        attr: 'content',
        prop: 'content',
        value: '',
    },
    {
        attr: 'cont-type',
        prop: 'contType',
        value: 'text',
    },
    {
        attr: 'cont-data',
        prop: 'contData',
        value: {},
    },
    {
        attr: 'ajax',
        prop: 'ajax',
        value: {},
    },
    {
        attr: 'tpl-str',
        prop: 'tplStr',
        value: '',
    },
    {
        attr: 'tpl-eng',
        prop: 'tplEng',
        value: null,
    },
    {
        attr: 'label',
        prop: 'label',
        value: '',
    },
    {
        attr: 'tips',
        prop: 'tips',
        value: '',
    },
    {
        attr: 'cube',
        prop: 'cube',
        value: '',
    },
    {
        attr: 'disk',
        prop: 'disk',
        value: '',
    },
    {
        attr: 'image',
        prop: 'image',
        value: '',
    },
    {
        attr: 'icon',
        prop: 'icon',
        value: '',
    },
    {
        attr: 'badge',
        prop: 'badge',
        value: null,
    },
    {
        attr: 'annot',
        prop: 'annot',
        value: '',
    },
    {
        attr: 'lamp',
        prop: 'lamp',
        value: '',
    },
    {
        attr: 'custom',
        prop: 'custom',
        value: '',
    },
    {
        attr: 'brief',
        prop: 'brief',
        value: '',
    },
    {
        attr: 'classes',
        prop: 'classes',
        value: '',
    },
    {
        attr: 'arrow',
        prop: 'arrow',
        value: {
            enable: false,
            show: `${prefix}icon-up`,
            hide: `${prefix}icon-up`,
            anim: `${prefix}rotate180`,
            type: 'icon',
            placement: 'end',
        },
    },
    {
        attr: 'tab',
        prop: 'tab',
        value: {
            enable: true,
        },
    },
    {
        attr: 'tools',
        prop: 'tools',
        value: {
            enable: false,
            children: ['toggle']
        },
    },
    {
        attr: 'deferred',
        prop: 'deferred',
        value: false,
    },
    {
        attr: 'feature',
        prop: 'feature',
        value: '',
    },
    {
        attr: 'dividable',
        prop: 'dividable',
        value: false,
    },
    {
        attr: 'collapsed',
        prop: 'collapsed',
        value: false,
    },
    {
        attr: 'head-closable',
        prop: 'headClosable',
        value: false,
    },
    {
        attr: 'padded',
        prop: 'padded',
        value: true,
    },
    {
        attr: 'theme',
        prop: 'theme',
        value: '',
    },
    {
        attr: 'b4-fill',
        prop: 'b4Fill',
        value: null,
    },
    {
        attr: 'b4-expand',
        prop: 'b4Expand',
        value: null,
    },
    {
        attr: 'b4-collapse',
        prop: 'b4Collapse',
        value: null,
    },
    {
        attr: 'on-filled',
        prop: 'onFilled',
        value: null
    },
    {
        attr: 'on-rendered',
        prop: 'onRendered',
        value: null
    },
    {
        attr: 'on-expand',
        prop: 'onExpand',
        value: null
    },
    {
        attr: 'on-expanded',
        prop: 'onExpanded',
        value: null
    },
    {
        attr: 'on-collapse',
        prop: 'onCollapse',
        value: null
    },
    {
        attr: 'on-collapsed',
        prop: 'onCollapsed',
        value: null
    },
    {
        attr: 'on-updatedcont',
        prop: 'onUpdatedCont',
        value: null
    },
    {
        attr: 'on-request',
        prop: 'onRequest',
        value: null
    },
    ...optBase
];

class Panel extends ModBaseListenCache {
    options = {};
    flatData;
    treeData;
    wrapEl;
    headEl;
    innerEl;
    captionEl;
    groupEl;
    labelEl;
    iconEl;
    diskEl;
    cubeEl;
    imageEl;
    briefEl;
    annotEl;
    badgeEl;
    tipsEl;
    customEl;
    toolsEl;
    arrowEl;
    bodyEl;
    contEl;
    closeEl;
    tabIns;
    contXhr;
    transmitioned;
    data;
    closeEvt;
    static hostType = 'node';
    static optMaps = optPanel;
    constructor(elem, options = {}, initial = true) {
        super();
        super.ready({
            options,
            host: elem,
            maps: Panel.optMaps,
            component: true,
            spread: ['arrow', 'tab', 'tools']
        });
        this.transmitioned = true;
        this.closeEvt = () => {
            if (!this.transmitioned)
                return;
            if (elState(this.bodyEl).isHidden) {
                this.expand();
            }
            else {
                this.collapse();
            }
        };
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch (err) {
            err ? console.error(err) : console.warn(config.warn.init);
            return this;
        }
        super.useTpl();
        this.renderMain();
        this.setAttrs();
        await this.getDataToRender();
        this.renderFinish();
        super.listen({ name: 'initiated', cb });
        return this;
    }
    correctOpts() {
        if (this.options.feature === 'window') {
            this.options.dividable = false;
        }
    }
    async fillBody(content, cb) {
        if (this.destroyed)
            return this;
        try {
            if (this.options.b4Fill) {
                let resp = await this.options.b4Fill.call(this, content);
                resp && (content = resp);
            }
        }
        catch (err) {
            err ? console.error(err) : console.warn('Body filling has been prevented!');
            return;
        }
        setContent({
            content,
            target: this.contEl,
            template: this.tplStr,
            engine: this.tplEng
        });
        if ((this.options.deferred || this.options.collapsed) && !this.options.arrow.enable && !this.options.headClosable) {
            await this.expand();
        }
        super.listen({ name: 'filled', cb, params: [content] });
        return this;
    }
    async getDataToRender(force = false) {
        let allow = force || !this.options.deferred;
        if (!allow)
            return;
        if (this.options.content) {
            this.data = await getContent.call(this, {
                content: this.options.content,
                contType: this.options.contType,
                contData: this.options.contData,
                ajax: {
                    xhrName: 'contXhr',
                    spinSel: this.arrowEl,
                    ...this.options.ajax
                },
                request: (data) => {
                    this.listen({ name: 'request', params: [data] });
                }
            });
        }
        else {
            this.data = this.rawEl || this.rawHtml;
        }
        this.data && await this.fillBody(this.data);
    }
    toggleArrow(val) {
        if (!this.arrowEl)
            return;
        let map = val ? { new: 'show', old: 'hide', action: 'remove' } : { new: 'hide', old: 'show', action: 'add' };
        if (this.options.arrow.type === 'image') {
            this.arrowEl.style.backgroundImage = `url("${this.options.arrow[map.new]}")`;
        }
        else {
            if (this.options.arrow.show === this.options.arrow.hide) {
                this.arrowEl.classList[map.action](this.options.arrow.anim);
            }
            else {
                classes(this.arrowEl).replace(this.options.arrow[map.old], this.options.arrow[map.new]);
            }
        }
    }
    setArrow() {
        if (!this.arrowEl)
            return;
        let prop = this.options.collapsed || this.options.deferred ? 'hide' : 'show';
        if (this.options.arrow.type === 'image') {
            this.options.arrow[prop] && (this.arrowEl.style.backgroundImage = `url("${this.options.arrow[prop]}")`);
        }
        else {
            this.options.arrow[prop] && this.arrowEl.classList.add(this.options.arrow[prop]);
            (this.options.arrow.show === this.options.arrow.hide) && prop === 'hide' && this.arrowEl.classList.add(this.options.arrow.anim);
        }
    }
    createHeadEl() {
        let tpl = `
        <div class="_panel-head">
                <div class="_panel-inner">
                    <div class="_panel-caption">
                        <div rep="group">
                            <i rep="label">${this.options.label}</i>
                        </div>
                    </div>
                </div>
            </div>
        `;
        this.headEl = tplToEl(tpl);
        this.innerEl = this.headEl.querySelector(`.${prefix}panel-inner`);
        this.captionEl = this.headEl.querySelector(`.${prefix}panel-caption`);
        this.groupEl = this.headEl.querySelector(`[${alias}="group"]`);
        this.labelEl = this.headEl.querySelector(`[${alias}="label"]`);
        if (this.options.icon) {
            this.iconEl = createEl('i', { [alias]: `icon`, class: this.options.icon });
            (this.options.feature === 'loose' ? this.innerEl : this.groupEl).insertAdjacentElement('beforebegin', this.iconEl);
        }
        if (this.options.disk) {
            this.diskEl = createEl('img', { [alias]: `disk`, src: this.options.disk });
            (this.options.feature === 'loose' ? this.innerEl : this.groupEl).insertAdjacentElement('beforebegin', this.diskEl);
        }
        if (this.options.cube) {
            this.cubeEl = createEl('img', { [alias]: `cube`, src: this.options.cube });
            (this.options.feature === 'loose' ? this.innerEl : this.groupEl).insertAdjacentElement('beforebegin', this.cubeEl);
        }
        if (this.options.image) {
            this.imageEl = createEl('img', { [alias]: `image`, src: this.options.image });
            (this.options.feature === 'loose' ? this.innerEl : this.groupEl).insertAdjacentElement('beforebegin', this.imageEl);
        }
        if (this.options.brief) {
            this.briefEl = createEl('div', { class: `${prefix}panel-brief` }, this.options.brief);
            this.captionEl.insertAdjacentElement('afterend', this.briefEl);
        }
        if (this.options.annot) {
            this.annotEl = createEl('span', { [alias]: `annot` }, this.options.annot);
            this.labelEl.insertAdjacentElement('afterend', this.annotEl);
        }
        if (!isNull(this.options.badge)) {
            this.badgeEl = createEl('ax-badge', { label: this.options.badge });
            this.groupEl.insertAdjacentElement('afterend', this.badgeEl);
        }
        if (this.options.tips) {
            this.tipsEl = createEl('span', { [alias]: `tips` }, this.options.tips);
            this.groupEl.insertAdjacentElement('afterend', this.tipsEl);
        }
        if (this.options.custom) {
            this.customEl = createEl('div', { [alias]: `custum` }, this.options.custom);
            this.groupEl.insertAdjacentElement('afterend', this.customEl);
        }
        if (this.options.tools.enable) {
            this.toolsEl = createTools(this.options.tools.children, this.captionEl);
            this.toolsEl.setAttribute(alias, 'tools');
        }
        if (this.options.arrow.enable) {
            this.arrowEl = createEl('i', { [alias]: `arrow` });
            this.setArrow();
            if (this.options.arrow.placement === 'end') {
                this.captionEl.appendChild(this.arrowEl);
            }
            else {
                (this.options.feature === 'loose' ? this.headEl : this.captionEl).insertAdjacentElement('afterbegin', this.arrowEl);
            }
        }
        this.wrapEl.insertAdjacentElement('afterbegin', this.headEl);
        this.options.dividable && this.headEl.insertAdjacentHTML('afterend', `<ax-line ${this.options.theme ? 'theme="' + this.options.theme + '"' : ''}></ax-line>`);
    }
    createBodyEl() {
        this.bodyEl = createEl('div', { class: `${prefix}panel-body` });
        if (this.options.padded) {
            this.contEl = createEl('div', { class: `${prefix}panel-inner` });
            this.bodyEl.appendChild(this.contEl);
        }
        else {
            this.contEl = this.bodyEl;
        }
        this.wrapEl.appendChild(this.bodyEl);
    }
    renderMain() {
        this.targetEl.innerHTML = '';
        this.wrapEl = createEl('div', { class: `${prefix}panel-wrap` });
        this.targetEl.appendChild(this.wrapEl);
        this.createHeadEl();
        this.createBodyEl();
        super.listen({ name: 'rendered' });
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}panel`);
        this.options.classes && classes(this.targetEl).add(this.options.classes);
        (this.options.collapsed || this.options.deferred) && this.targetEl.toggleAttribute('collapsed', true);
        this.options.headClosable && this.targetEl.toggleAttribute('headclosable', true);
        this.options.feature && this.targetEl.setAttribute('feature', this.options.feature);
        this.options.lamp && this.targetEl.setAttribute('lamp', this.options.lamp);
        this.options.theme && this.targetEl.setAttribute('theme', this.options.theme);
    }
    renderFinish() {
        this.closeEl = this.options.headClosable ? this.headEl : this.arrowEl;
        if (this.closeEl) {
            this.closeEl.removeEventListener('click', this.closeEvt);
            this.closeEl.addEventListener('click', this.closeEvt, false);
        }
        for (let k of this.options.tools.children) {
            if (k.name === 'toggle') {
                k.wrapEl.onclick = () => {
                    if (k.iconEl.classList.contains(k.icon)) {
                        this.targetEl.toggleAttribute('max', true);
                        classes(k.iconEl).replace(k.icon, k.swap);
                    }
                    else {
                        this.targetEl.toggleAttribute('max', false);
                        classes(k.iconEl).replace(k.swap, k.icon);
                    }
                };
            }
            else {
                k.action && k.action.call(this, k);
            }
        }
        if (this.options.tab.enable && this.headEl.querySelector(`.${prefix}tab-head`) && this.bodyEl.querySelector(`.${prefix}tab-body`)) {
            this.tabIns = new Tab(this.targetEl, this.options.tab);
        }
    }
    async expand(cb) {
        if (this.destroyed || !this.transmitioned || elState(this.bodyEl).isVisible)
            return this;
        try {
            if (this.options.b4Expand) {
                await this.options.b4Expand.call(this);
            }
        }
        catch (err) {
            err ? console.error(err) : console.warn('Body expanding has been prevented!');
            return;
        }
        this.options.deferred && !this.contEl.innerHTML && await this.getDataToRender(true);
        slideDown({
            el: this.bodyEl,
            before: () => {
                this.transmitioned = false;
                super.listen({ name: 'expand' });
            },
            doing: () => {
                this.toggleArrow(true);
            },
            done: () => {
                this.transmitioned = true;
                super.listen({ name: 'expanded', cb });
                super.updateCache({ collapsed: false });
            }
        });
        return this;
    }
    async collapse(cb) {
        if (this.destroyed || !this.transmitioned || elState(this.bodyEl).isHidden)
            return this;
        try {
            if (this.options.b4Collapse) {
                await this.options.b4Collapse.call(this);
            }
        }
        catch (err) {
            err ? console.error(err) : console.warn('Body collapsing has been prevented!');
            return;
        }
        slideUp({
            el: this.bodyEl,
            before: () => {
                this.transmitioned = false;
                super.listen({ name: 'collapse' });
            },
            doing: () => {
                this.toggleArrow(false);
            },
            done: () => {
                this.transmitioned = true;
                super.listen({ name: 'collapsed', cb });
                super.updateCache({ collapsed: true });
            }
        });
        return this;
    }
    
    destroy(cb) {
        if (this.destroyed)
            return this;
        this.closeEl && this.closeEl.removeEventListener('click', this.closeEvt);
        this.contXhr && this.contXhr.abort();
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

const optDrag = [
    {
        attr: 'routes',
        prop: 'routes',
        value: [],
    },
    {
        attr: 'current',
        prop: 'current',
        value: '',
    },
    {
        attr: 'redirect',
        prop: 'redirect',
        value: [],
    },
    {
        attr: 'before-check',
        prop: 'beforeCheck',
        value: null,
    },
    {
        attr: 'after-check',
        prop: 'afterCheck',
        value: null,
    },
    {
        attr: 'purpose',
        prop: 'purpose',
        value: 'sort',
    },
    {
        attr: 'point',
        prop: 'point',
        value: {
            before: ['t/3'],
            after: ['b/3'],
        },
    },
    {
        attr: 'holder',
        prop: 'holder',
        value: {
            from: false,
            to: false,
        },
    },
    {
        attr: 'arrow',
        prop: 'arrow',
        value: {
            enable: false,
            icon: `${prefix}icon-right`,
            placement: 'left',
            selector: '',
        },
    },
    {
        attr: 'gesture',
        prop: 'gesture',
        value: {},
    },
    {
        attr: 'delay',
        prop: 'delay',
        value: 50,
    },
    {
        attr: 'duration',
        prop: 'duration',
        value: 200,
    },
    {
        attr: 'b4-drop',
        prop: 'b4Drop',
        value: null,
    },
    {
        attr: 'on-dropping',
        prop: 'onDropping',
        value: null,
    },
    {
        attr: 'on-dropped',
        prop: 'onDropped',
        value: null,
    },
    {
        attr: 'on-dragstart',
        prop: 'onDragStart',
        value: null,
    },
    {
        attr: 'on-dragmove',
        prop: 'onDragMove',
        value: null,
    },
    {
        attr: 'on-dragend',
        prop: 'onDragEnd',
        value: null,
    },
    {
        attr: 'on-located',
        prop: 'onLocated',
        value: null,
    },
    ...optBase
];

class Router extends ModBaseListen {
    options = {};
    routes;
    current;
    stateEvt;
    static hostType = 'none';
    constructor(options = {}, initial = true) {
        super();
        super.ready({
            options,
            maps: optDrag,
        });
        this.routes = this.options.routes || [];
        this.current = this.options.current;
        this.stateEvt = (evt) => {
            if (!evt.state) ;
            else {
                if (this.current !== evt.state.path) {
                    let toRoute = this.getRoute(evt.state.path), fromRoute = this.getRoute();
                    if (this.options.beforeCheck) {
                        let resp = this.options.beforeCheck(toRoute, fromRoute);
                        if (typeof resp === 'string') {
                            let reRoute = this.getRoute(resp);
                            if (reRoute) {
                                this.push(reRoute);
                            }
                            else {
                                return;
                            }
                        }
                        else if (!resp) {
                            return;
                        }
                    }
                    this.current = evt.state.path;
                    toRoute.action && toRoute.action.call(this, toRoute);
                    super.listen({ name: 'located', params: [toRoute] });
                }
            }
        };
        super.listen({ name: 'constructed' });
        initial && this.init();
    }
    async init(cb) {
        super.listen({ name: 'initiate' });
        try {
            this.options.b4Init && await this.options.b4Init.call(this);
        }
        catch {
            console.warn(config.warn.init);
            return this;
        }
        window.addEventListener('popstate', this.stateEvt);
        this.current = this.getUrlPath();
        this.add({ path: this.current, action: null });
        super.listen({ name: 'initiated', cb });
        return this;
    }
    getUrlPath() {
        let pathname = window.location.pathname, search = window.location.search, hash = window.location.hash;
        return pathname + search + hash;
    }
    getRoute(path = this.current) {
        return this.routes.find((k) => k.path === path);
    }
    ;
    add(state) {
        let item = this.routes.find((k) => k.path === state.path);
        item ? Object.assign(item, state) : this.routes.push(state);
    }
    remove(path) {
        this.routes.filter((k) => k.path !== path);
    }
    push(state, cb) {
        if (isNull(state) || this.destroyed)
            return;
        if (this.options.beforeCheck) {
            let resp = this.options.beforeCheck(state, this.getRoute());
            if (typeof resp === 'string') {
                state = this.getRoute(state.resp);
                if (!state)
                    window.location.href = resp;
            }
            else if (!resp) {
                return;
            }
        }
        let route = this.getRoute(state.path);
        if (route) {
            this.replace(state);
            Object.assign(route, state);
            return this;
        }
        this.add(state);
        history.pushState({ path: state.path }, Date.now() + '', state.path);
        this.current = state.path;
        state.action && state.action.call(this, state);
        super.listen({ name: 'located', cb, params: [state] });
        return this;
    }
    replace(state, cb) {
        if (isNull(state) || this.destroyed)
            return;
        let route = this.getRoute(state.path);
        if (!route) {
            this.push(state);
            return this;
        }
        Object.assign(route, state);
        if (this.options.beforeCheck) {
            let resp = this.options.beforeCheck(route, this.getRoute());
            if (typeof resp === 'string') {
                state = this.getRoute(state.resp);
                if (!state)
                    return;
            }
            else if (!resp) {
                return;
            }
        }
        history.replaceState({ path: state.path }, Date.now() + '', state.path);
        this.current = state.path;
        state.action && state.action.call(this, state);
        super.listen({ name: 'located', cb, params: [state] });
        return this;
    }
    back() {
        if (this.destroyed)
            return;
        history.go(-1);
        return this;
    }
    forward() {
        if (this.destroyed)
            return;
        history.go(1);
        return this;
    }
    go(path) {
        if (this.destroyed)
            return;
        if (typeof path === 'string') {
            this.push(this.getRoute(path));
        }
        else {
            history.go(path);
        }
        return this;
    }
    clear() {
        this.routes = [];
        return this;
    }
    routeChange(path) {
        if (!path)
            return;
        let action = this.routes[path];
        if (action) {
            this.current = path;
            action.call(this);
        }
        else {
            console.error(`No route found for path: ${path}`);
        }
    }
    beforeCheck(to, from, next) {
    }
    afterCheck() {
    }
    restore() {
        return this;
    }
    destroy(cb) {
        if (this.destroyed)
            return;
        this.destroyed = true;
        super.listen({ name: 'destroyed', cb });
        return this;
    }
}

class CompBaseCommField extends CompBaseComm {
    name;
    value;
    disabled;
    readOnly;
    checked;
    multiple;
    events;
    inputEl;
    zeroEvt;
    evtsArr;
    toolsEl;
    constructor() {
        super();
        this.focus = () => {
            this.inputEl.focus();
        };
        this.blur = () => {
            this.inputEl.blur();
        };
        this.zeroEvt = new Event('input');
        this.reset = () => {
            for (let k in this.propsRaw) {
                (this.propsRaw[k] !== null && this.propsRaw[k] !== false) ? this.setAttribute(k, this.propsRaw[k]) : this.removeAttribute(k);
            }
            this.inputEl && this.inputEl.dispatchEvent(this.zeroEvt);
            this.listen({ name: 'reset' });
        };
        this.clear = () => {
            if (this.inputEl) {
                this.inputEl.value = '';
                this.inputEl.dispatchEvent(this.zeroEvt);
            }
            this.listen({ name: 'cleared' });
        };
        this.on('connected', () => {
            this.setAttribute(ax.compSign, '');
            this.setCache();
        });
        this.on('reset', () => {
            this.clearCache();
        });
    }
    static evtsArr = [...this.baseAttrs, 'on-cleared', 'on-changed',];
    connectedCallback() {
        if (this.inputEl) {
            this.addEvts(['blur', 'focus']);
            for (let k in this.events) {
                this.inputEl.addEventListener(k, () => {
                    this.dispatchEvent(this.events[k].event);
                });
            }
        }
        this.connectedRender();
    }
    addEvts(evts) {
        this.events = {};
        if (isEmpty(evts))
            return;
        for (let k of evts) {
            this.events[k] = createEvt({ target: this, name: k });
        }
    }
    getToolsEl(data) {
        if (!isEmpty(data)) {
            this.toolsEl = createTools(data, null, this);
            this.toolsEl.setAttribute(alias, 'tools');
        }
    }
    setFieldProps(props) {
        for (let k of props) {
            k === 'name' ? this.name = this.propsProxy.name || '' :
                k === 'value' ? this.value = this.propsProxy.value || '' :
                    k === 'disabled' ? this.disabled = this.propsProxy.disabled || false :
                        k === 'readOnly' ? this.readOnly = this.propsProxy.readonly || false :
                            k === 'multiple' ? this.multiple = this.propsProxy.multiple || false :
                                k === 'check' ? this.checked = (this.propsProxy.check === 'ed' ? true : false) : null;
        }
    }
    getItemsData(data) {
        let arr;
        if (isEmpty(data)) {
            let tmp = createEl('div', null, this.rawHtml), script = tmp.querySelector('script[type="content"]');
            arr = this.getAttribute('content') ? getAttrArr(this.getAttribute('content')) : getAttrArr((script?.innerHTML || this.rawHtml).replace(/\n/g, '').trim());
        }
        else {
            arr = Array.isArray(data) ? data : getAttrArr(data);
        }
        return this.formatData(arr);
    }
    formatData(data) {
        let itemType = typeof data[0];
        if (itemType === 'string') {
            return data.map((k) => {
                return { label: k, value: k, disabled: false, checked: false };
            });
        }
        else {
            return data;
        }
    }
    addScrollable(input) {
        let scrolled = input.scrollHeight > input.clientHeight;
        this.wrapEl.toggleAttribute('scrollable', scrolled);
    }
    setValAttr(data, el) {
        if (!isNull(data.value)) {
            el.value = data.value;
        }
        else if (isNull(data.value) && data.label) {
            el.value = data.label;
        }
    }
}

class CompBaseCommFieldMixin extends CompBaseCommField {
    modsOpts;
    static custAttrs;
    static boolAttrs;
    constructor() {
        super();
        this.createShadow();
        this.modsOpts = {};
        this.on('disconnected', () => {
            this.ins && this.ins.destroy();
        });
        this.on('adopted', () => {
            this.ins && this.ins.destroy();
        });
    }
    static get observedAttributes() {
        return ['value', ...this.custAttrs, ...this.boolAttrs, ...this.evtsArr];
    }
    connectedCallback() {
        this.addEvts(['blur', 'focus', 'input', 'change']);
        this.connectedRender();
    }
    mapVals(name, newVal, module, opts) {
        return module.evtsArr.includes(name) ? newVal :
            module.boolAttrs.includes(name) ? Object.values(attrJoinVal(name, newVal, opts))[0] :
                isNull(newVal) ? null : Object.values(attrJoinVal(name, newVal, opts))[0];
    }
    saveModsOpts(name, opts, module = 'module') {
        if (!name || !module)
            return;
        let tmp = this.propsProxy[name], item = opts.find((k) => k.attr === name);
        if (!item)
            return;
        !this.modsOpts[module] && (this.modsOpts[module] = {});
        if (isNull(tmp)) {
            this.connected && (this.modsOpts[module][item.prop] = item.value);
        }
        else {
            this.modsOpts[module][item.prop] = tmp;
        }
    }
}

class MoreElem extends CompBaseComm {
    constructor() {
        super();
        this.createShadow();
        super.createPropsObs();
    }
    static get observedAttributes() {
        return [...optMore.map((k) => k.attr), 'options'];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        super.updateProxy(name, newVal, optMore);
    }
    render() {
        this.insertSource();
        this.wrapEl = createEl('div', { [alias]: 'slot-host' }, this.rawHtml);
        this.appendChild(this.wrapEl);
        this.ins = new More(this.wrapEl);
        setTimeout(() => {
            this.propsObs.on('completed', (data) => {
                this.ins.update(this.properties);
                this.listen({ name: 'updated', params: [this.properties] });
            });
        }, 500);
    }
}

class ResultElem extends CompBaseComm {
    iconEl;
    labelEl;
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
        
    }
    static custAttrs = ['to', 'shape', 'type', ...this.baseAttrs];
    static boolAttrs = [];
    static get observedAttributes() {
        return ['label', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, ResultElem);
    }
    getRawData() {
        this.getRawProps(ResultElem);
        this.propsRaw.label = this.getAttribute('label') || this.rawHtml;
        this.getProxyProps();
    }
    fillWrap(data) {
        this.wrapEl = createEl('span', { [alias]: 'wrap' });
        this.iconEl = createEl('i', { [alias]: 'icon' }, icons.svg.info);
        this.labelEl = createEl('i', { [alias]: 'label' }, data.label || '');
        this.wrapEl.appendChild(this.iconEl);
        data.label && this.wrapEl.appendChild(this.labelEl);
    }
    getIcon(data) {
        let result = '';
        if (!data.to) {
            result = icons.svg.info;
        }
        else {
            if (!data.type || data.type === 'svg') {
                result = icons.svg[data.to];
            }
            else if (data.type === 'font') {
                let icon = data.to + (data.shape === 'thick' ? '-t' : data.shape === 'fill' ? '-f' : '');
                result = icons.font[icon];
            }
        }
        return result;
    }
    render(data) {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
    changedMaps = {
        label: this.changedLabel,
        type: this.changedIcon,
        to: this.changedIcon,
        shape: this.changedIcon,
    };
    changedLabel(opt) {
        if (opt.newVal) {
            this.labelEl.innerHTML = opt.newVal || '';
            elState(this.labelEl).isVirtual && this.wrapEl.appendChild(this.labelEl);
        }
        else {
            this.labelEl.remove();
        }
    }
    changedIcon(opt) {
        let content = this.getIcon(this.propsProxy);
        content && (this.iconEl.innerHTML = content);
    }
}

class DeformElem extends CompBaseComm {
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['type', 'theme', 'shape', 'size', ...this.baseAttrs];
    static boolAttrs = ['disabled', 'bordered', 'filled', 'dark'];
    static get observedAttributes() {
        return ['content', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, DeformElem);
    }
    getRawData() {
        this.getRawProps(DeformElem);
        this.getProxyProps();
    }
    fillWrap(data) {
        this.wrapEl = createEl('div', { [alias]: 'wrap' }, '<i></i>'.repeat(3));
    }
    render(data) {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
    changedMaps = {
        class: this.changedClass,
    };
    changedClass(opt) {
        let has = opt.newVal.includes(config.actClass), type = this.propsRaw.type;
        if (has) {
            if (type === 'left') {
                this.setAttribute('type', 'right');
            }
            else if (type === 'right') {
                this.setAttribute('type', 'left');
            }
            else if (type === 'up') {
                this.setAttribute('type', 'down');
            }
            else if (type === 'down') {
                this.setAttribute('type', 'up');
            }
            else {
                this.setAttribute('type', 'close');
            }
        }
        else {
            this.setAttribute('type', this.propsRaw.type);
        }
    }
}

class RadioElem extends CompBaseCommField {
    labelEl;
    legendEl;
    check;
    type;
    constructor() {
        super();
        this.type = 'radio-comp';
        this.getRawData();
        this.fillWrap(this.propsProxy);
        this.clear = () => {
            this.removeAttribute('check');
            this.listen({ name: 'cleared' });
        };
        this.check = (val) => {
            this.setAttribute('check', val);
        };
    }
    static custAttrs = ['size', 'value', 'name', 'type', 'check', ...this.evtsArr];
    static boolAttrs = ['disabled'];
    static get observedAttributes() {
        return ['label', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.saveProps(name, newVal, RadioElem);
        if (['disabled', 'value', 'name'].includes(name)) {
            if (name === 'value') {
                if (newVal === null && !this.propsProxy.label) {
                    this.propsProxy.value = undefined;
                    this.value = '';
                    this.inputEl.removeAttribute('value');
                }
                else {
                    this.propsProxy.value = this.value = newVal;
                    this.inputEl.value = this.value;
                }
            }
            else if (name === 'name') {
                newVal ? this.inputEl.name = newVal : this.inputEl.removeAttribute('name');
                this.name = this.inputEl.name;
            }
            else {
                this.inputEl.toggleAttribute('disabled', this.propsProxy.disabled);
                this.disabled = this.propsProxy.disabled;
            }
        }
        else if (name === 'label') {
            this.labelEl.innerHTML = newVal;
            if (newVal && isNull(this.propsProxy.value)) {
                this.propsProxy.value = this.value = newVal;
                this.inputEl.value = this.value;
            }
        }
        else if (name === 'check') {
            let oldChecked = oldVal === 'ed' ? true : false;
            this.checked = newVal === 'ed' ? true : false;
            this.inputEl.checked = this.checked;
            this.listen({ name: 'check', params: [{ value: newVal, checked: this.checked }] });
            newVal && this.listen({ name: 'cleared' });
            oldChecked !== this.checked && this.listen({ name: 'changed', params: [{ newVal: this.checked, oldVal: !this.checked }] });
        }
    }
    getRawData() {
        this.getRawProps(RadioElem);
        this.propsRaw.label = this.getAttribute('label') || this.rawHtml;
        this.getProxyProps();
        this.setFieldProps(['name', 'value', 'disabled', 'check']);
    }
    fillWrap(data) {
        this.wrapEl = createEl('label', { [alias]: 'wrap' });
        this.inputEl = createEl('input', { type: 'radio', [ax.embedSign]: '' });
        this.setValAttr(data, this.inputEl);
        data.name && (this.inputEl.name = data.name || '');
        this.legendEl = createEl('u');
        this.labelEl = createEl('i', '', data.label);
        this.wrapEl.append(this.inputEl, this.legendEl, this.labelEl);
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
        this.inputEl.addEventListener('change', (e) => {
            this.setAttribute('check', 'ed');
            let others = getEls(`ax-radio[name=${this.propsProxy.name}]`, this.closest('form')).filter((k) => k !== this);
            others.forEach((k) => {
                k.setAttribute('check', '');
            });
        }, false);
    }
}

class CheckboxElem extends CompBaseCommField {
    labelEl;
    legendEl;
    check;
    type;
    constructor() {
        super();
        this.type = 'checkbox-comp';
        this.getRawData();
        this.fillWrap(this.propsProxy);
        this.clear = () => {
            this.removeAttribute('check');
            this.listen({ name: 'cleared' });
        };
        this.check = (val) => {
            this.setAttribute('check', val);
        };
    }
    static custAttrs = ['size', 'value', 'type', 'name', 'check', ...this.evtsArr];
    static boolAttrs = ['disabled'];
    static get observedAttributes() {
        return ['label', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.saveProps(name, newVal, CheckboxElem);
        if (['disabled', 'value', 'name'].includes(name)) {
            if (name === 'value') {
                if (newVal === null && !this.propsProxy.label) {
                    this.propsProxy.value = undefined;
                    this.value = '';
                    this.inputEl.removeAttribute('value');
                }
                else {
                    this.propsProxy.value = this.value = newVal;
                    this.inputEl.value = this.value;
                }
            }
            else if (name === 'name') {
                newVal ? this.inputEl.name = newVal : this.inputEl.removeAttribute('name');
                this.name = this.inputEl.name;
            }
            else {
                this.inputEl.toggleAttribute('disabled', this.propsProxy.disabled);
                this.disabled = this.propsProxy.disabled;
            }
        }
        else if (name === 'label') {
            this.labelEl.innerHTML = newVal;
            if (newVal && isNull(this.propsProxy.value)) {
                this.propsProxy.value = this.value = newVal;
                this.inputEl.value = this.value;
            }
        }
        else if (name === 'check') {
            let oldChecked = oldVal === 'ed' ? true : false;
            this.checked = newVal === 'ed' ? true : false;
            this.inputEl.checked = this.checked;
            this.listen({ name: 'check', params: [{ value: newVal, checked: this.checked }] });
            oldChecked !== this.checked && this.listen({ name: 'changed', params: [{ newVal: this.checked, oldVal: !this.checked }] });
        }
    }
    getRawData() {
        [...CheckboxElem.custAttrs, ...CheckboxElem.lazyAttrs].forEach((k) => {
            this.propsRaw[k] = this.getAttribute(k);
        });
        CheckboxElem.boolAttrs.forEach((k) => {
            this.propsRaw[k] = getAttrBool(this.getAttribute(k));
        });
        this.propsRaw.label = this.getAttribute('label') || this.rawHtml;
        for (let k in this.propsRaw) {
            this.propsProxy[k] = this.propsRaw[k];
        }
        this.setFieldProps(['name', 'value', 'disabled', 'check']);
    }
    fillWrap(data) {
        this.wrapEl = createEl('label', { [alias]: 'wrap' });
        this.inputEl = createEl('input', { type: 'checkbox', [ax.embedSign]: '' });
        this.setValAttr(data, this.inputEl);
        data.name && (this.inputEl.name = data.name || '');
        this.legendEl = createEl('u');
        this.labelEl = createEl('i', '', data.label);
        this.wrapEl.append(this.inputEl, this.legendEl, this.labelEl);
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
        this.inputEl.addEventListener('change', (e) => {
            let val = e.target.checked ? 'ed' : '';
            this.setAttribute('check', val);
        }, false);
    }
}

class BtnElem extends CompBaseComm {
    labelEl;
    iconEl;
    tailEl;
    diskEl;
    cubeEl;
    imageEl;
    badgeEl;
    meanEl;
    tipsIns;
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['type', 'theme', 'href', 'tab', 'target', 'rel', 'download', 'shape', 'size', 'width', 'check', 'icon', 'tail', 'disk', 'cube', 'image', 'tips', 'badge', 'mean', ...this.baseAttrs];
    static boolAttrs = ['disabled', 'shaded', 'grad'];
    static get observedAttributes() {
        return ['label', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, BtnElem);
    }
    getRawData() {
        this.getRawProps(BtnElem);
        this.initLabel();
        this.getProxyProps();
    }
    initLabel() {
        if (this.innerHTML) {
            let child = this.firstElementChild;
            if (child && child.nodeName === 'A') {
                this.propsRaw.href = child.href;
                child.hasAttribute('target') && (this.propsRaw.target = child.target);
                child.hasAttribute('rel') && (this.propsRaw.rel = child.rel);
                child.hasAttribute('download') && (this.propsRaw.download = child.download);
                this.propsRaw.label = child.innerHTML;
            }
            else {
                this.propsRaw.label = this.innerHTML;
            }
        }
        else {
            this.propsRaw.label = this.getAttribute('label');
        }
    }
    getWrap(data) {
        let obj = { [alias]: 'wrap' }, node = '';
        if (!isNull(data.href)) {
            node = 'a';
            obj.href = data.href;
            data.target && (obj.target = data.target);
            data.rel && (obj.rel = data.rel);
        }
        else {
            node = 'span';
        }
        this.wrapEl = createEl(node, obj, this.labelEl);
    }
    insertIcon(val) {
        this.iconEl = createEl('i', { class: val, [alias]: 'icon' });
        this.labelEl.insertAdjacentElement('beforeBegin', this.iconEl);
    }
    insertTail(val) {
        this.tailEl = createEl('i', { class: val, [alias]: 'tail' });
        this.labelEl.insertAdjacentElement('afterEnd', this.tailEl);
    }
    insertMean(val) {
        this.meanEl = createEl('i', { [alias]: val });
        this.wrapEl.appendChild(this.meanEl);
    }
    insertBadge(val) {
        if (!isNull(val) && val !== '0') {
            this.badgeEl = createEl('ax-badge', { [alias]: 'badge' }, val?.trim());
            this.wrapEl.appendChild(this.badgeEl);
        }
    }
    insertDisk(val) {
        if (val) {
            this.diskEl = createEl('img', { [alias]: 'disk', src: val });
            this.labelEl.insertAdjacentElement('beforeBegin', this.diskEl);
        }
    }
    insertCube(val) {
        if (val) {
            this.cubeEl = createEl('img', { [alias]: 'cube', src: val });
            this.labelEl.insertAdjacentElement('beforeBegin', this.cubeEl);
        }
    }
    insertImage(val) {
        if (val) {
            this.imageEl = createEl('img', { [alias]: 'image', src: val });
            this.labelEl.insertAdjacentElement('beforeBegin', this.imageEl);
        }
    }
    insertTips(val) {
        if (val) {
            this.tipsIns = new Tooltip(this, {
                content: val,
                trigger: 'sticky',
                deadShow: true,
            });
        }
    }
    fillWrap(data) {
        this.labelEl = createEl('i', { [alias]: 'label' }, data.label);
        this.getWrap(data);
        data.icon ? this.insertIcon(data.icon) : null;
        data.tail ? this.insertTail(data.tail) : null;
        data.mean ? this.insertMean(data.mean) : null;
        data.cube ? this.insertCube(data.cube) : null;
        data.image ? this.insertImage(data.image) : null;
        data.disk ? this.insertDisk(data.disk) : null;
        data.hasOwnProperty('badge') ? this.insertBadge(data.badge) : null;
    }
    render(data) {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
    changedMaps = {
        label: this.changedLabel,
        href: this.changedHref,
        target: this.changedTarget,
        rel: this.changedTarget,
        download: this.changedTarget,
        tab: this.changedTab,
        icon: this.changedIcon,
        disk: this.changedDisk,
        cube: this.changedCube,
        image: this.changedImage,
        tips: this.changedTips,
        tail: this.changedTail,
        mean: this.changedMean,
        badge: this.changedBadge,
    };
    changedLabel(opt) {
        this.labelEl.innerHTML = opt.newVal || '';
    }
    changedHref(opt) {
        if (opt.newVal === null || opt.oldVal === null) {
            this.innerHTML = '';
            this.fillWrap(this.propsProxy);
            this.render();
        }
        else {
            this.wrapEl.href = opt.newVal;
        }
    }
    changedTarget(opt) {
        if (opt.newVal) {
            this.wrapEl.setAttribute(opt.name, opt.newVal);
        }
        else {
            this.wrapEl.removeAttribute(opt.name);
        }
    }
    changedTab(opt) {
        opt.newVal ? this.wrapEl.tabIndex = opt.newVal : this.wrapEl.removeAttribute('tabindex');
    }
    changedIcon(opt) {
        if (this.iconEl) {
            this.iconEl.setAttribute('class', opt.newVal);
        }
        else {
            this.insertIcon(opt.newVal);
        }
    }
    changedTail(opt) {
        if (this.tailEl) {
            this.tailEl.setAttribute('class', opt.newVal);
        }
        else {
            this.insertTail(opt.newVal);
        }
    }
    changedDisk(opt) {
        if (this.diskEl) {
            this.diskEl.setAttribute('src', opt.newVal);
        }
        else {
            this.insertDisk(opt.newVal);
        }
    }
    changedCube(opt) {
        if (this.cubeEl) {
            this.cubeEl.setAttribute('src', opt.newVal);
        }
        else {
            this.insertCube(opt.newVal);
        }
    }
    changedImage(opt) {
        if (this.imageEl) {
            this.imageEl.setAttribute('src', opt.newVal);
        }
        else {
            this.insertImage(opt.newVal);
        }
    }
    changedMean(opt) {
        if (this.meanEl) {
            this.meanEl.setAttribute(alias, opt.newVal);
        }
        else {
            this.insertMean(opt.newVal);
        }
    }
    changedBadge(opt) {
        if (this.badgeEl) {
            isNull(opt.newVal) || opt.newVal === '0' ? this.badgeEl.remove() : this.badgeEl.setAttribute('label', opt.newVal);
        }
        else {
            this.insertBadge(opt.newVal);
        }
    }
    changedTips(opt) {
        if (this.tipsIns) {
            this.tipsIns.updateCont(opt.newVal);
            if (opt.newVal) {
                this.tipsIns.popupIns.state !== 'shown' && this.tipsIns.popupIns.show();
            }
            else {
                this.tipsIns.popupIns.hide();
            }
        }
        else {
            opt.newVal ? this.insertTips(this.propsProxy.tips) : null;
        }
    }
}

class LineElem extends CompBaseComm {
    labelEl;
    startEl;
    endEl;
    contRaw;
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['type', 'theme', 'size', 'align', 'dir', ...this.baseAttrs];
    static boolAttrs = [];
    static get observedAttributes() {
        return ['label', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, LineElem);
    }
    getRawData() {
        this.getRawProps(LineElem);
        this.propsRaw.label = this.getAttribute('label') || this.rawHtml;
        this.getProxyProps();
    }
    fillWrap(data) {
        this.wrapEl = createEl('div', { [alias]: 'wrap' }, this.labelEl);
        this.startEl = createEl('i', { [alias]: 'start' });
        this.endEl = createEl('i', { [alias]: 'end' });
        this.labelEl = createEl('i', { [alias]: 'label' }, data.label);
        this.wrapEl.appendChild(this.startEl);
        data.label ? this.wrapEl.append(this.labelEl, this.endEl) : null;
    }
    render(data) {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
    changedMaps = {
        label: this.changedLabel,
    };
    changedLabel(opt) {
        this.labelEl.innerHTML = opt.newVal;
        if (opt.newVal) {
            (elState(this.labelEl).isVirtual) && this.wrapEl.append(this.labelEl, this.endEl);
        }
        else {
            this.labelEl.remove();
            this.endEl.remove();
        }
    }
}

class AvatarElem extends CompBaseComm {
    mainEl;
    badgeEl;
    tipsEl;
    contRaw;
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['type', 'theme', 'tips', 'size', 'src', 'shape', 'badge', 'href', 'target', 'rel', ...this.baseAttrs];
    static boolAttrs = ['disabled'];
    static get observedAttributes() {
        return ['src', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, AvatarElem);
    }
    getMainEl(type, src) {
        return (type === 'text') ? createEl('i', { [alias]: 'main' }, src || 'null') : createEl('img', { [alias]: 'main', src: src || getImgAvatar() });
    }
    getRawData() {
        this.getRawProps(AvatarElem);
        this.propsRaw.src = this.getAttribute('src') || this.rawHtml;
        this.getProxyProps();
    }
    fillWrap(data) {
        this.wrapEl = createEl('a', { [alias]: 'wrap' });
        this.badgeEl = createEl('ax-badge', { [alias]: 'badge', shadowed: '', label: data?.badge?.trim() });
        this.tipsEl = createEl('i', { [alias]: 'tips' }, data.tips || '');
        this.mainEl = this.getMainEl(data.type, data.src);
        this.wrapEl.appendChild(this.mainEl);
        this.hasAttribute('badge') && this.wrapEl.appendChild(this.badgeEl);
        this.getAttribute('tips') && this.wrapEl.appendChild(this.tipsEl);
    }
    render(data) {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
    changedMaps = {
        badge: this.changedBadge,
        tips: this.changedTips,
        src: this.changedSrc,
        href: this.changedHref,
        target: this.changedHref,
        rel: this.changedHref,
        type: this.changedType,
    };
    changedSrc(opt) {
        (this.propsProxy.type === 'text') ? this.mainEl.innerHTML = opt.newVal : this.mainEl.src = opt.newVal;
    }
    changedHref(opt) {
        if (opt.newVal) {
            this.wrapEl.setAttribute(opt.name, opt.newVal);
        }
        else {
            this.wrapEl.removeAttribute(opt.name);
        }
    }
    changedBadge(opt) {
        if (opt.newVal === null) {
            this.badgeEl.remove();
        }
        else {
            this.badgeEl.setAttribute('label', (opt.newVal === '0' || !opt.newVal) ? '' : opt.newVal);
            (elState(this.badgeEl).isVirtual) ? this.wrapEl.appendChild(this.badgeEl) : null;
        }
    }
    changedTips(opt) {
        if (opt.newVal === null) {
            this.tipsEl.remove();
        }
        else {
            this.tipsEl.innerHTML = opt.newVal;
            (elState(this.tipsEl).isVirtual) ? this.wrapEl.appendChild(this.tipsEl) : null;
        }
    }
    changedType(opt) {
        if ((this.mainEl.nodeName === 'IMG' && opt.newVal === 'text') || (this.mainEl.nodeName === 'I' && (opt.newVal === 'image' || !opt.newVal))) {
            this.mainEl.remove();
            this.mainEl = this.getMainEl(opt.newVal, this.propsProxy.src);
            this.wrapEl.appendChild(this.mainEl);
        }
    }
}

class FormatElem extends CompBaseComm {
    tipsIns;
    infoEl;
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['type', 'rts', 'info', 'tips', 'lines', 'size', 'width', ...this.baseAttrs];
    static boolAttrs = [];
    static get observedAttributes() {
        return ['label', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, FormatElem);
    }
    getRawData() {
        this.getRawProps(FormatElem);
        this.propsRaw.label = this.getAttribute('label') || this.rawHtml;
        this.getProxyProps();
    }
    fillWrap(data) {
        this.wrapEl = createEl('div', { [alias]: 'wrap' });
        this.fillCont(data);
    }
    fillCont(data) {
        if (data.type === 'ruby') {
            this.wrapEl.innerHTML = `<ruby>${this.getRuby(data.label, data.rts)}</ruby>`;
        }
        else if (data.type === 'latex') {
            try {
                new Function(`label`, `el`, `"use strict";${data.engin || 'katex.render(label, el)'}`)(data.label, this.wrapEl);
            }
            catch {
                console.error('Please introduce a math formula parsing plugin(katex)!');
            }
        }
        else if (data.type === 'prefix' || data.type === 'suffix') {
            this.infoEl = createEl('i', { [alias]: 'info' }, data.info);
            this.wrapEl.innerHTML = data.label;
            data.type === 'prefix' ? this.wrapEl.insertAdjacentElement('afterbegin', this.infoEl) : this.wrapEl.appendChild(this.infoEl);
            this.tipsIns = new Tooltip(this.infoEl, {
                content: data.tips,
                asleep: true,
            });
        }
        else if (data.type === 'break') {
            this.wrapEl.innerHTML = this.getBrText(data.label, data.lines, data.size);
        }
        else {
            this.wrapEl.innerHTML = data.label;
        }
    }
    render(data) {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
    getRuby(text, rts) {
        let result = '';
        if (!text) {
            return result;
        }
        if (!rts) {
            return text;
        }
        let chars = text.split(''), hyphen = text.includes(config.splitHyphen) ? config.splitHyphen : config.wordHyphen, rtArr = rts.split(hyphen);
        chars = chars.map((k, i) => {
            return `${k}<rt>${rtArr[i] || ''}</rt>`;
        });
        result = chars.join('');
        return result;
    }
    getBrText(text, lines = 2, size) {
        if (!text) {
            return '';
        }
        if (lines === 1 || !lines) {
            return text;
        }
        let points = Math.ceil(text.length / lines), fontSize = size ? 'style="font-size:' + size + '"' : '', result = `<i class="${prefix}t-br" ${fontSize}>`, hasBr = text.includes('<br') ? true : false;
        if (hasBr) {
            result += text;
        }
        else {
            for (let i = 0; i < lines; i++) {
                if (i === lines - 1) {
                    result += text.substring(i * points);
                }
                else {
                    result += text.substring(i * points, (i + 1) * points) + '<br/>';
                }
            }
        }
        return result + '</i>';
    }
    changedMaps = {
        label: this.changedLabel,
        type: this.changedLabel,
        rts: this.changedRts,
        info: this.changedInfo,
        tips: this.changedTips,
        lines: this.changedLines,
        size: this.changedSize,
        width: this.changedWidth,
    };
    changedLabel(opt) {
        this.wrapEl.innerHTML = '';
        this.fillCont(this.propsProxy);
    }
    changedRts(opt) {
        if (this.propsProxy.type === 'ruby') {
            this.wrapEl.innerHTML = `<ruby>${this.getRuby(this.propsProxy.label, opt.newVal)}</ruby>`;
        }
    }
    changedInfo(opt) {
        if (this.propsProxy.type === 'prefix' || this.propsProxy.type === 'suffix') {
            this.infoEl.innerHTML = opt.newVal;
        }
    }
    changedTips(opt) {
        if (this.propsProxy.type === 'prefix' || this.propsProxy.type === 'suffix') {
            this.tipsIns.update(!opt.newVal ? { asleep: true } : { asleep: false, content: opt.newVal });
        }
    }
    changedLines(opt) {
        if (this.propsProxy.type === 'break') {
            this.wrapEl.innerHTML = this.getBrText(this.propsProxy.label, ~~opt.newVal, this.propsProxy.size);
        }
    }
    changedSize(opt) {
        if (this.propsProxy.type === 'break') {
            let tmp = this.wrapEl.firstElementChild;
            tmp ? tmp.style.fontSize = opt.newVal : null;
        }
    }
    changedWidth(opt) {
        if (this.propsProxy.type === 'break') {
            let tmp = this.wrapEl.firstElementChild;
            tmp ? tmp.style.width = opt.newVal : null;
        }
    }
}

class FlagElem extends CompBaseComm {
    labelEl;
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['type', 'theme', 'placement', ...this.baseAttrs];
    static boolAttrs = [];
    static get observedAttributes() {
        return ['label', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, FlagElem);
    }
    getRawData() {
        this.getRawProps(FlagElem);
        this.propsRaw.label = this.getAttribute('label') || this.rawHtml;
        this.getProxyProps();
    }
    fillWrap(data) {
        this.labelEl = createEl('i', { [alias]: 'label' }, this.propsProxy.label);
        this.wrapEl = createEl('span', { [alias]: 'wrap' }, this.labelEl);
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
    changedMaps = {
        label: this.changedLabel,
    };
    changedLabel(opt) {
        this.labelEl.innerHTML = opt.newVal || '';
    }
}

class BuoyElem extends CompBaseComm {
    headEl;
    labelEl;
    iconEl;
    arrowEl;
    valueEl;
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['icon', 'label', 'theme', 'size', ...this.baseAttrs];
    static boolAttrs = ['inverted', 'arrow'];
    static get observedAttributes() {
        return ['value', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, BuoyElem);
    }
    getRawData() {
        this.getRawProps(BuoyElem);
        this.propsRaw.value = this.getAttribute('value') || this.rawHtml;
        this.getProxyProps();
    }
    fillWrap(data) {
        this.wrapEl = createEl('span', { [alias]: 'wrap' });
        this.headEl = createEl('span', { [alias]: 'head' });
        this.labelEl = createEl('i', { [alias]: 'label' }, data.label || '');
        this.iconEl = createEl('i', { class: data.icon || '', [alias]: 'icon' });
        this.arrowEl = createEl('i', { class: `${prefix}icon-down`, [alias]: 'arrow' });
        this.valueEl = createEl('i', { [alias]: 'value' }, data.value || '');
        data.label ? this.headEl.appendChild(this.labelEl) : null;
        data.icon ? this.headEl.insertAdjacentElement('afterBegin', this.iconEl) : null;
        data.arrow ? this.headEl.insertAdjacentElement('beforeEnd', this.arrowEl) : null;
        this.wrapEl.append(this.headEl, this.valueEl);
    }
    render(data) {
        this.insertSource();
        this.append(this.wrapEl);
    }
    changedMaps = {
        value: this.changedValue,
        label: this.changedLabel,
        icon: this.changedIcon,
        arrow: this.changedArrow,
    };
    changedValue(opt) {
        this.valueEl.innerHTML = opt.newVal || '';
    }
    changedLabel(opt) {
        if (opt.newVal === null) {
            this.labelEl.remove();
        }
        else {
            this.labelEl.innerHTML = opt.newVal;
            if (elState(this.labelEl).isVirtual) {
                if (!elState(this.iconEl).isVirtual) {
                    this.iconEl.insertAdjacentElement('afterEnd', this.labelEl);
                }
                else {
                    this.headEl.insertAdjacentElement('afterBegin', this.labelEl);
                }
            }
        }
    }
    changedIcon(opt) {
        if (opt.newVal === null) {
            this.iconEl.remove();
        }
        else {
            this.iconEl.setAttribute('class', opt.newVal);
            if (elState(this.iconEl).isVirtual) {
                this.headEl.insertAdjacentElement('afterBegin', this.iconEl);
            }
        }
    }
    changedArrow(opt) {
        if (!attrValBool(opt.newVal)) {
            this.arrowEl.remove();
        }
        else {
            if (elState(this.arrowEl).isVirtual) {
                this.headEl.appendChild(this.arrowEl);
            }
        }
    }
}

class GoodElem extends CompBaseComm {
    labelEl;
    iconEl;
    tipsEl;
    valueEl;
    increase;
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
        this.increase = (step = 1, cb) => {
            let value = parseInt(this.properties.value) + step, tips = this.getAttribute('tips');
            this.canListen = false;
            this.setAttribute('value', String(value));
            tips && this.setAttribute('tips', tips);
            this.toggleAttribute('increased', true);
            cb && cb.call(this, value);
            this.canListen = true;
        };
    }
    static custAttrs = ['icon', 'label', 'tips', ...this.baseAttrs];
    static boolAttrs = ['disabled', 'increased'];
    static get observedAttributes() {
        return ['value', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, GoodElem);
    }
    getRawData() {
        this.getRawProps(GoodElem);
        this.propsRaw.value = this.getAttribute('value') || this.rawHtml;
        this.getProxyProps();
    }
    fillWrap(data) {
        this.wrapEl = createEl('span', { [alias]: 'wrap' });
        this.labelEl = createEl('i', { [alias]: 'label' });
        this.iconEl = createEl('i', { [alias]: 'icon' });
        this.valueEl = createEl('i', { [alias]: 'value' }, data.value || '');
        this.tipsEl = createEl('i', { [alias]: 'tips' });
        this.wrapEl.appendChild(this.valueEl);
    }
    render(data) {
        this.insertSource();
        this.append(this.labelEl, this.wrapEl, this.tipsEl);
    }
    changedMaps = {
        label: this.changedLabel,
        value: this.changedLabel,
        tips: this.changedLabel,
        icon: this.changedIcon,
    };
    changedLabel(opt) {
        this[opt.name + 'El'].innerHTML = opt.newVal || '';
    }
    changedIcon(opt) {
        if (opt.newVal === null) {
            this.iconEl.remove();
        }
        else {
            this.iconEl.setAttribute('class', opt.newVal);
            elState(this.iconEl).isVirtual && this.wrapEl.insertAdjacentElement('afterBegin', this.iconEl);
        }
    }
}

class AnchorsElem extends CompBaseComm {
    data;
    smoothEvent;
    interactIns;
    constructor() {
        super();
        this.getRawData();
        this.createInteract();
        this.fillWrap(this.propsProxy);
        this.render(this.propsProxy);
    }
    static custAttrs = ['root', 'active', 'offset', ...this.baseAttrs];
    static boolAttrs = ['smooth'];
    static get observedAttributes() {
        return ['headings', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, AnchorsElem);
    }
    getRawData() {
        this.getRawProps(AnchorsElem);
        this.propsRaw.headings = this.getHeadings(this.getAttribute('headings') || this.rawHtml);
        this.getProxyProps();
    }
    createInteract() {
        this.interactIns = new IntersectionObserver(entries => {
            for (let k of entries) {
                if (k.isIntersecting) {
                    if (k.intersectionRatio === 1) {
                        this.activate(k.target, true, false);
                    }
                }
            }
        }, {
            threshold: [1],
        });
    }
    getHeadings(str) {
        return valToArr(str).map((k) => {
            let tmp = k.split('|');
            return { heading: tmp[0], exclude: tmp[1] };
        });
    }
    getData(headings) {
        if (headings.length === 0) {
            headings = [{ heading: 'h2', exclude: '' }];
        }
        let result = [], getChildren = (index, current, arr, pNext) => {
            let els = getEls(current.heading, this.propsProxy.root), excEls = getEls(current.exclude, this.propsProxy.root), keyNext = headings[index + 1]?.heading;
            for (let [y, x] of els.entries()) {
                let obj = {
                    target: x,
                    label: x.textContent.replace('§', '')
                };
                let children = getBetweenEls({
                    selector: keyNext,
                    start: x,
                    end: els[y + 1] || pNext,
                    root: this.propsProxy.root,
                });
                if (children.length > 0) {
                    obj.children = [];
                    getChildren(index + 1, headings[index + 1], obj.children, els[y + 1]);
                }
                if (excEls.length && excEls.find((i) => i.contains(x)))
                    continue;
                arr.push(obj);
            }
        };
        getChildren(0, headings[0], result);
        treeTools.addIdPath({ source: result });
        return result;
    }
    fillWrap(data) {
        this.wrapEl = createEl('div', { [alias]: 'wrap' });
        this.get2FillTreeNodes(data);
    }
    get2FillTreeNodes(data) {
        this.data = this.getData(data.headings);
        let plantTree = (parent, data) => {
            let ul = createEl('ul', { class: `${prefix}reset` });
            for (let k of data) {
                k.wrapEl = createEl('li', { [alias]: 'wrap' });
                k.labelEl = createEl('a', { [alias]: 'label', href: `#${k.label}` }, k.label);
                k.headEl = createEl('div', { [alias]: 'head', id: k.id }, k.labelEl);
                k.wrapEl.appendChild(k.headEl);
                k.labelEl.onclick = () => this.smoothToActive(k, false);
                k.target.id = k.label;
                k.target.classList.add(`${prefix}anchor`);
                let tmp = k.target.querySelector(`${prefix}hash`);
                tmp && tmp.remove();
                k.hashEl = createEl('a', { href: `#${k.label}`, class: `${prefix}hash` }, '§');
                k.hashEl.onclick = () => this.smoothToActive(k, false);
                k.target.appendChild(k.hashEl);
                this.interactIns.observe(k.target);
                if (k.hasOwnProperty('children')) {
                    k.bodyEl = plantTree(k.wrapEl, k.children);
                    k.bodyEl.setAttribute(alias, 'body');
                }
                ul.appendChild(k.wrapEl);
            }
            parent.appendChild(ul);
            return ul;
        };
        this.wrapEl.innerHTML = '';
        plantTree(this.wrapEl, this.data);
    }
    render(data) {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
    activate(data, active = true, smooth = true) {
        if (!data) {
            return this;
        }
        let flat = treeTools.toFlat(this.data), dataType = getDataType(data), tmp = Number(data), value = tmp === 0 || !!tmp ? tmp : data, item = dataType.includes('HTML') ? findItem(data, flat, 'target') : findItem(value, flat);
        if (item) {
            if (!item.active && active) {
                item.headEl.setAttribute('active', '');
                item.active = true;
                let others = flat.filter((k) => k !== item && k.active);
                for (let k of others) {
                    k.headEl.removeAttribute('active');
                    Reflect.deleteProperty(k, 'active');
                }
            }
            if (smooth) {
                if (this.propsProxy.smooth) {
                    scrollTo(item.target, {
                        offset: this.propsProxy.offset,
                    });
                }
            }
        }
    }
    addSmooth() {
        let flat = treeTools.toFlat(this.data);
        for (let k of flat) {
            k.labelEl.onclick = () => this.smoothToActive(k, false);
            k.hashEl.onclick = () => this.smoothToActive(k, false);
        }
    }
    removeSmooth() {
        let flat = treeTools.toFlat(this.data);
        for (let k of flat) {
            k.labelEl.onclick = null;
            k.hashEl.onclick = null;
        }
    }
    smoothToActive(obj, active = true) {
        this.activate(obj.target, active, this.propsProxy.smooth);
    }
    changedMaps = {
        headings: this.changedRender,
        root: this.changedRender,
        active: this.changedActive,
        offset: this.changedOffset,
        smooth: this.changedSmooth,
    };
    changedRender(opt) {
        this.get2FillTreeNodes(this.propsProxy);
    }
    changedActive(opt) {
        this.activate(opt.newVal);
    }
    changedOffset(opt) {
        for (let k of this.data)
            k.target.style.scrollMarginTop = opt.newVal;
    }
    changedSmooth(opt) {
        let bool = attrValBool(opt.newVal);
        bool ? this.addSmooth() : this.removeSmooth();
    }
}

class MenuElem extends CompBaseComm {
    constructor() {
        super();
        this.createShadow();
        super.createPropsObs();
    }
    static get observedAttributes() {
        return [...optMenu$1.map((k) => k.attr), 'options'];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (name === 'active') {
            this.ins && newVal && this.ins.activate(newVal);
        }
        if (name === 'disable') {
            this.ins && newVal && this.ins.treeIns.disable(newVal);
        }
        else {
            super.updateProxy(name, newVal, optMenu$1);
        }
    }
    render() {
        this.insertSource();
        this.wrapEl = createEl('div', { [alias]: 'slot-host' }, this.rawHtml);
        this.appendChild(this.wrapEl);
        this.ins = new Menu(this.wrapEl);
        setTimeout(() => {
            this.propsObs.on('completed', (data) => {
                this.ins.update(this.properties);
                this.listen({ name: 'updated', params: [this.properties] });
            });
        }, 500);
    }
}

class FileElem extends CompBaseCommField {
    holderEl;
    meanEl;
    clickEvent;
    changeEvt;
    namesEl;
    type;
    clickEvt;
    labelEl;
    constructor() {
        super();
        this.type = 'file-comp';
        this.getRawData();
        this.fillWrap(this.propsProxy);
        this.clickEvt = (evt) => {
            let target = getEvtTarget(evt);
            if (this.toolsEl && this.toolsEl.contains(target))
                return;
            this.inputEl.click();
        };
        this.zeroEvt = new Event('change');
    }
    static custAttrs = ['name', 'value', 'accept', 'size', 'label', 'tools', ...this.evtsArr];
    static boolAttrs = ['disabled', 'readonly', 'multiple', 'full'];
    static get observedAttributes() {
        return ['placeholder', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, FileElem, { tools: getAttrArr(newVal, 'close') });
    }
    getRawData() {
        this.getRawProps(FileElem);
        this.propsRaw.placeholder = this.getAttribute('placeholder') || this.rawHtml || config.lang.form.fileLabel;
        this.propsRaw.value = this.propsRaw.multiple ? [] : '';
        this.getProxyProps();
        this.propsProxy['tools'] = getAttrArr(this.getAttribute('tools'), 'close');
        this.value = this.propsProxy.value;
        this.setFieldProps(['name', 'value', 'disabled', 'readonly', 'multiple']);
    }
    fillWrap(data) {
        this.wrapEl = createEl('div', { [alias]: 'wrap' });
        this.inputEl = createEl('input', { type: 'file', [ax.embedSign]: '' });
        this.inputEl.multiple = data.multiple;
        this.labelEl = createEl('span', { [alias]: 'label' });
        data.name && (this.inputEl.name = (data.name + (this.inputEl.multiple ? `[]` : '')) || '');
        super.getToolsEl(data.tools);
        this.holderEl = createEl('i', { [alias]: 'placeholder' }, data.placeholder);
        this.namesEl = createEl('i', { [alias]: 'names' });
        this.meanEl = createEl('i', { [alias]: 'mean' }, 'file');
        this.wrapEl.append(this.inputEl, this.namesEl, this.holderEl, this.meanEl);
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
        this.inputEl.addEventListener('change', (e) => {
            let files = [...this.inputEl.files], names = files.map((k) => k.name), oldVal = this.value;
            this.value = this.propsProxy.multiple ? files : files[0] || '';
            files.length > 0 && (this.propsProxy.value = names.join(','));
            this.namesEl.innerHTML = (files.length === 0) ? '' : (files.length === 1) ? this.inputEl.value : renderTpl(this.propsProxy.lang?.multi || config.lang.form.fileMulti, { data: files.length }) + this.propsProxy.value;
            this.listen({ name: 'changed', params: [{ oldVal, newVal: this.value }] });
        }, false);
        this.wrapEl.addEventListener('click', this.clickEvt);
    }
    changedMaps = {
        disabled: this.changedBool,
        readonly: this.changedBool,
        multiple: this.changedBool,
        full: this.changedBool,
        name: this.changedName,
        value: this.changedValue,
        accept: this.changedName,
        tools: this.changedTools,
        placeholder: this.changedHolder,
        label: this.changedLabel,
    };
    changedBool(opt) {
        this.inputEl[opt.name === 'readonly' ? 'readOnly' : opt.name] = this.propsProxy[opt.name];
        this[opt.name === 'readonly' ? 'readOnly' : opt.name] = this.propsProxy[opt.name];
    }
    changedTools(opt) {
        this.toolsEl && this.toolsEl.remove();
        if (this.propsProxy.tools.length > 0) {
            super.getToolsEl(this.propsProxy.tools);
            this.propsProxy.tools.forEach((k) => {
                if (k.name === 'close') {
                    k.wrapEl.onclick = () => {
                        this.clear();
                    };
                }
                else {
                    k.action && k.action.bind(this);
                }
            });
            this.holderEl.insertAdjacentElement('afterEnd', this.toolsEl);
        }
    }
    changedHolder(opt) {
        this.holderEl.innerHTML = opt.newVal;
    }
    changedName(opt) {
        this.inputEl.setAttribute(opt.name, opt.newVal);
        opt.name === 'name' && (this.name = opt.newVal + (this.propsProxy.multiple ? `[]` : ''));
        if (opt.name === 'name') {
            this.name = opt.newVal || '';
            if (this.name) {
                this.inputEl.name = this.propsProxy.multiple ? `${this.name}[]` : this.name;
            }
            else {
                this.inputEl.removeAttribute('name');
            }
        }
    }
    changedValue(opt) {
        this.clear();
        console.warn('The field.files property cannot be changed through scripts,this operation is performed by resetting!');
    }
    changedLabel(opt) {
        if (opt.newVal) {
            this.labelEl.innerHTML = opt.newVal;
            elState(this.labelEl).isVirtual && this.wrapEl.insertAdjacentElement('afterbegin', this.labelEl);
        }
        else {
            this.labelEl.remove();
        }
    }
}

class InputElem extends CompBaseCommField {
    unitEl;
    customEl;
    imageEl;
    labelEl;
    limitEl;
    diskEl;
    cubeEl;
    iconEl;
    btnEl;
    meanEl;
    type;
    select;
    btnEvt;
    constructor() {
        super();
        this.type = 'text-comp';
        this.getRawData();
        this.fillWrap(this.propsProxy);
        this.select = () => this.inputEl.select();
        this.btnEvt = (evt) => {
            if (evt.code === 'Enter' && elState(this.btnEl).isVisible && this.btnEl.click && !this.propsProxy.blocked) {
                preventDft(evt);
                this.btnEl.click();
            }
        };
    }
    static custAttrs = ['name', 'placeholder', 'type', 'size', 'limit', 'tools', 'icon', 'cube', 'disk', 'image', 'btn', 'action', 'label', 'unit', 'custom', 'mean', 'task', ...this.evtsArr];
    static boolAttrs = ['disabled', 'readonly', 'blocked', 'full'];
    static get observedAttributes() {
        return ['value', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, InputElem, { tools: getAttrArr(newVal, 'close') });
    }
    getRawData() {
        this.getRawProps(InputElem);
        this.propsRaw.value = this.getAttribute('value') || this.rawHtml;
        this.getProxyProps();
        this.propsProxy['tools'] = getAttrArr(this.getAttribute('tools'), 'close');
        this.setFieldProps(['name', 'value', 'disabled', 'readOnly']);
    }
    fillWrap(data) {
        this.wrapEl = createEl('div', { [alias]: 'wrap' });
        this.inputEl = createEl('input', { type: data.type || 'text', placeholder: data.placeholder || config.lang.form.placeholder, [ax.embedSign]: '' });
        data.value && (this.inputEl.value = data.value || '');
        data.name && (this.inputEl.name = data.name || '');
        this.wrapEl.appendChild(this.inputEl);
        super.getToolsEl(data.tools);
        this.limitEl = createEl('div', { [alias]: 'limit' });
        this.labelEl = createEl('span', { [alias]: 'label' });
        this.unitEl = createEl('span', { [alias]: 'unit' });
        this.diskEl = createEl('img', { [alias]: 'disk' });
        this.cubeEl = createEl('img', { [alias]: 'cube' });
        this.imageEl = createEl('img', { [alias]: 'image' });
        this.iconEl = createEl('i', { [alias]: 'icon' });
        this.btnEl = createEl('span', { [alias]: 'btn' });
        this.customEl = createEl('span', { [alias]: 'custom' });
        this.meanEl = createEl('i', { [alias]: 'mean' });
    }
    updateLimit() {
        let total = ~~this.propsProxy.limit, value = this.propsProxy.value, len = value.length, tmp = total - len, remaining = tmp <= 0 ? 0 : tmp;
        if (!total) {
            return this;
        }
        this.limitEl.innerHTML = renderTpl(this.propsProxy.lang?.max || config.lang.form.maxLength, { total, value: len, remaining });
        if (tmp <= 0) {
            if (tmp < 0) {
                this.inputEl.value = value.slice(0, total);
                this.inputEl.dispatchEvent(this.zeroEvt);
            }
            this.setAttribute('exceeded', '');
        }
        else {
            this.removeAttribute('exceeded');
        }
        elState(this.limitEl).isVirtual && this.appendChild(this.limitEl);
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
        ~~this.propsProxy.limit && this.appendChild(this.limitEl);
        this.inputEl.addEventListener('input', (e) => {
            let tmp = this.propsProxy.value;
            this.propsProxy.value = this.value = this.inputEl.value;
            this.updateLimit();
            let data = { oldVal: tmp, newVal: this.inputEl.value };
            this.listen({ name: 'input', params: [data] });
            (tmp !== this.inputEl.value) && this.listen({ name: 'changed', params: [data] });
        }, false);
    }
    changedMaps = {
        disabled: this.changedBool,
        readonly: this.changedBool,
        blocked: this.changedBool,
        full: this.changedBool,
        name: this.changedName,
        type: this.changedName,
        placeholder: this.changedName,
        value: this.changedValue,
        icon: this.changedIcon,
        disk: this.changedImage,
        cube: this.changedImage,
        image: this.changedImage,
        task: this.changedTask,
        label: this.changedLabel,
        unit: this.changedUnit,
        custom: this.changedCustom,
        action: this.changedAction,
        btn: this.changedBtn,
        mean: this.changedMean,
        limit: this.changedLimit,
        tools: this.changedTools,
    };
    changedBool(opt) {
        if (opt.name === 'blocked') {
            if (this.propsProxy['blocked']) {
                this.btnEl.setAttribute('disabled', '');
            }
            else {
                this.btnEl.removeAttribute('disabled');
            }
        }
        else {
            this.inputEl[opt.name === 'readonly' ? 'readOnly' : opt.name] = this.propsProxy[opt.name];
            this[opt.name === 'readonly' ? 'readOnly' : opt.name] = this.propsProxy[opt.name];
        }
    }
    changedTools(opt) {
        this.toolsEl && this.toolsEl.remove();
        if (this.propsProxy.tools.length > 0) {
            super.getToolsEl(this.propsProxy.tools);
            this.propsProxy.tools.forEach((k) => {
                if (k.name === 'close') {
                    k.wrapEl.onclick = () => this.clear();
                }
                else {
                    k.action && k.action.bind(this);
                }
            });
            this.inputEl.insertAdjacentElement('afterend', this.toolsEl);
        }
    }
    changedValue(opt) {
        this.value = opt.newVal || '';
        let tmp = this.inputEl.value;
        this.inputEl.value = opt.newVal;
        (tmp !== this.value) && this.listen({ name: 'changed', params: [{ oldVal: tmp, newVal: this.value }] });
    }
    changedLimit(opt) {
        this.updateLimit();
    }
    changedName(opt) {
        if (opt.name === 'type' && !['text', 'number', 'search', 'email', 'url', 'tel', 'password', 'time', 'week', 'datetime', 'datetime-local', 'month'].includes(opt.newVal))
            return;
        isNull(opt.newVal) ? this.inputEl.removeAttribute(opt.name) : this.inputEl.setAttribute(opt.name, opt.newVal);
        opt.name === 'name' && (this.name = opt.newVal || '');
    }
    changedLabel(opt) {
        if (opt.newVal) {
            this.labelEl.innerHTML = opt.newVal;
            elState(this.labelEl).isVirtual && this.wrapEl.insertAdjacentElement('afterbegin', this.labelEl);
        }
        else {
            this.labelEl.remove();
        }
    }
    changedUnit(opt) {
        if (opt.newVal) {
            this.unitEl.innerHTML = opt.newVal;
            elState(this.unitEl).isVirtual && this.wrapEl.appendChild(this.unitEl);
        }
        else {
            this.unitEl.remove();
        }
    }
    changedCustom(opt) {
        if (opt.newVal) {
            this.customEl.innerHTML = opt.newVal;
            if (elState(this.customEl).isVirtual) {
                elState(this.unitEl).isVisible ? this.unitEl.insertAdjacentElement('beforebegin', this.customEl) : this.wrapEl.appendChild(this.customEl);
            }
        }
        else {
            this.customEl.remove();
        }
    }
    changedBtn(opt) {
        if (opt.newVal) {
            this.btnEl.innerHTML = opt.newVal;
            if (elState(this.btnEl).isVirtual) {
                elState(this.unitEl).isVisible ? this.unitEl.insertAdjacentElement('beforebegin', this.btnEl) : this.wrapEl.appendChild(this.btnEl);
            }
            this.inputEl.addEventListener('keydown', this.btnEvt, false);
        }
        else {
            this.btnEl.remove();
            this.inputEl.removeEventListener('keydown', this.btnEvt);
        }
    }
    changedAction(opt) {
        if (opt.newVal) {
            this.btnEl.onclick = () => {
                new Function('"use strict";return ' + opt.newVal)().call(this, { btn: this.btnEl, el: this });
            };
        }
        else {
            this.btnEl.onclick = null;
        }
    }
    changedIcon(opt) {
        if (opt.newVal) {
            this.iconEl.classList.add(opt.newVal);
            elState(this.iconEl).isVirtual && this.inputEl.insertAdjacentElement('beforebegin', this.iconEl);
        }
        else {
            this.iconEl.remove();
        }
    }
    changedImage(opt) {
        if (opt.newVal) {
            this[`${opt.name}El`].src = opt.newVal;
            elState(this[`${opt.name}El`]).isVirtual && this.inputEl.insertAdjacentElement('beforebegin', this[`${opt.name}El`]);
        }
        else {
            this[`${opt.name}El`].remove();
        }
    }
    changedMean(opt) {
        if (opt.newVal) {
            this.meanEl.classList.add(opt.newVal);
            if (elState(this.meanEl).isVirtual) {
                elState(this.unitEl).isVisible ? this.unitEl.insertAdjacentElement('beforebegin', this.meanEl) : this.wrapEl.appendChild(this.meanEl);
            }
        }
        else {
            this.meanEl.remove();
        }
    }
    changedTask(opt) {
        if (opt.newVal) {
            this.meanEl.onclick = () => {
                new Function('"use strict";return ' + opt.newVal)().call(this, { btn: this.meanEl, el: this });
            };
        }
        else {
            this.meanEl.onclick = null;
        }
    }
}

class TextareaElem extends CompBaseCommField {
    labelEl;
    limitEl;
    type;
    select;
    headEl;
    meanEl;
    constructor() {
        super();
        this.type = 'textarea-comp';
        this.getRawData();
        this.fillWrap(this.propsProxy);
        this.select = () => this.inputEl.select();
    }
    static custAttrs = ['name', 'placeholder', 'size', 'tools', 'limit', 'label', 'mean', 'task', ...this.evtsArr];
    static boolAttrs = ['disabled', 'readonly', 'single', 'full'];
    static get observedAttributes() {
        return ['value', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, TextareaElem, { tools: getAttrArr(newVal, 'close') });
    }
    getRawData() {
        this.getRawProps(TextareaElem);
        this.propsRaw.value = this.getAttribute('value') || this.rawHtml;
        this.getProxyProps();
        this.propsProxy['tools'] = getAttrArr(this.getAttribute('tools'), 'close');
        this.setFieldProps(['name', 'value', 'disabled', 'readOnly']);
    }
    fillWrap(data) {
        this.wrapEl = createEl('div', { [alias]: 'wrap' });
        this.inputEl = createEl('textarea', { placeholder: data.placeholder || config.lang.form.placeholder, [ax.embedSign]: '' });
        data.value && (this.inputEl.value = data.value || '');
        data.name && (this.inputEl.name = data.name || '');
        this.wrapEl.appendChild(this.inputEl);
        this.getToolsEl(data.tools);
        this.limitEl = createEl('div', { [alias]: 'limit' });
        this.labelEl = createEl('span', { [alias]: 'label' });
        this.headEl = createEl('div', { [alias]: 'head' }, this.labelEl);
        this.meanEl = createEl('i', { [alias]: 'mean' });
    }
    updateLimit() {
        let total = ~~this.propsProxy.limit, value = this.propsProxy.value, len = value.length, tmp = total - len, remaining = tmp <= 0 ? 0 : tmp;
        if (!total) {
            return this;
        }
        this.limitEl.innerHTML = renderTpl(this.propsProxy.lang?.max || config.lang.form.maxLength, { total, value: len, remaining });
        if (tmp <= 0) {
            if (tmp < 0) {
                this.inputEl.value = value.slice(0, total);
                this.inputEl.dispatchEvent(this.zeroEvt);
            }
            this.setAttribute('exceeded', '');
        }
        else {
            this.removeAttribute('exceeded');
        }
        elState(this.limitEl).isVirtual && this.appendChild(this.limitEl);
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
        ~~this.propsProxy.limit && this.appendChild(this.limitEl);
        this.inputEl.addEventListener('input', (e) => {
            let tmp = this.propsProxy.value;
            this.propsProxy.value = this.inputEl.value;
            this.value = this.inputEl.value;
            this.updateLimit();
            this.addScrollable(this.inputEl);
            let data = { oldVal: tmp, newVal: this.inputEl.value };
            this.listen({ name: 'input', params: [data] });
            (tmp !== this.inputEl.value) && this.listen({ name: 'changed', params: [data] });
        }, false);
    }
    changedMaps = {
        disabled: this.changedBool,
        readonly: this.changedBool,
        full: this.changedBool,
        name: this.changedName,
        placeholder: this.changedName,
        value: this.changedValue,
        tools: this.changedTools,
        label: this.changedLabel,
        mean: this.changedMean,
        single: this.changedSingle,
        task: this.changedTask,
    };
    changedBool(opt) {
        this.inputEl[opt.name === 'readonly' ? 'readOnly' : opt.name] = this.propsProxy[opt.name];
        this[opt.name === 'readonly' ? 'readOnly' : opt.name] = this.propsProxy[opt.name];
    }
    changedName(opt) {
        isNull(opt.newVal) ? this.inputEl.removeAttribute(opt.name) : this.inputEl.setAttribute(opt.name, opt.newVal);
        opt.name === 'name' && (this.name = opt.newVal || '');
    }
    changedTools(opt) {
        this.toolsEl && this.toolsEl.remove();
        if (this.propsProxy.tools.length > 0) {
            this.getToolsEl(this.propsProxy.tools);
            this.propsProxy.tools.forEach((k) => {
                if (k.name === 'close') {
                    k.wrapEl.onclick = () => this.clear();
                }
                else {
                    k.action && k.action.bind(this);
                }
            });
            if (!this.propsProxy['single'] && this.propsProxy['label']) {
                this.headEl.appendChild(this.toolsEl);
            }
            else {
                this.inputEl.insertAdjacentElement('afterend', this.toolsEl);
            }
        }
    }
    changedValue(opt) {
        this.value = opt.newVal || '';
        let tmp = this.inputEl.value;
        this.inputEl.value = this.value;
        (tmp !== this.value) && this.listen({ name: 'changed', params: [{ oldVal: tmp, newVal: this.value }] });
    }
    changedLabel(opt) {
        if (opt.newVal) {
            this.labelEl.innerHTML = opt.newVal;
            elState(this.headEl).isVirtual && this.wrapEl.insertAdjacentElement('afterbegin', this.headEl);
            !this.propsProxy.single && this.propsProxy.tools.length > 0 && this.headEl.appendChild(this.toolsEl);
            this.propsProxy.mean && this.headEl.insertAdjacentElement('afterbegin', this.meanEl);
        }
        else {
            this.headEl.remove();
            this.propsProxy.tools.length > 0 && this.wrapEl.appendChild(this.toolsEl);
            this.propsProxy.mean && this.propsProxy.single && this.wrapEl.appendChild(this.meanEl);
        }
    }
    changedSingle(opt) {
        if (this.propsProxy.single) {
            this.propsProxy.tools.length > 0 && this.wrapEl.appendChild(this.toolsEl);
            this.propsProxy.mean && this.wrapEl.appendChild(this.meanEl);
        }
        else {
            if (this.propsProxy.label) {
                this.propsProxy.mean && this.headEl.insertAdjacentElement('afterbegin', this.meanEl);
                this.propsProxy.tools.length > 0 && this.headEl.appendChild(this.toolsEl);
            }
            else {
                this.propsProxy.tools.length > 0 && this.wrapEl.appendChild(this.toolsEl);
                this.meanEl.remove();
            }
        }
    }
    changedMean(opt) {
        if (opt.newVal) {
            this.meanEl.classList.add(opt.newVal);
            if (elState(this.meanEl).isVirtual) {
                if (!this.propsProxy.single) {
                    this.headEl.insertAdjacentElement('afterbegin', this.meanEl);
                }
                else {
                    this.wrapEl.appendChild(this.meanEl);
                }
            }
        }
        else {
            this.meanEl.remove();
        }
    }
    changedTask(opt) {
        if (opt.newVal) {
            this.meanEl.onclick = () => {
                new Function('"use strict";return ' + opt.newVal)().call(this, { btn: this.meanEl, el: this });
            };
        }
        else {
            this.meanEl.onclick = null;
        }
    }
}

class RadiosElem extends CompBaseCommField {
    labelEl;
    legendEl;
    inputs;
    setVals;
    setLabels;
    check;
    type;
    valMap;
    constructor() {
        super();
        this.type = 'radios-comp';
        this.getRawData();
        this.fillWrap(this.propsProxy);
        this.reset = () => {
            if (this.propsRaw.content != this.propsRaw.content)
                this.updateInputs(this.propsRaw.content, this.propsRaw);
            for (let k in this.propsRaw) {
                if (k === 'content')
                    continue;
                (this.propsRaw[k] !== null && this.propsRaw[k] !== false) ? this.setAttribute(k, this.propsRaw[k]) : this.removeAttribute(k);
            }
            this.listen({ name: 'reset' });
        };
        this.clear = () => {
            this.removeAttribute('checked');
            this.listen({ name: 'cleared' });
        };
        this.check = (value, type = 'ed', from = 'value') => {
            let val = Array.isArray(value) ? value[0] : value;
            if (!val && val !== 0) {
                this.setAttribute('checked', '');
                return;
            }
            let input = this.inputs.find((i) => i.propsProxy[from] == val);
            if (input) {
                if (type === 'ing') {
                    input.checked && this.setAttribute('checked', '');
                    input.setAttribute('check', type);
                }
                else if (type === 'ed') {
                    this.setAttribute('checked', input.propsProxy.value);
                }
                else {
                    input.checked && this.setAttribute('checked', '');
                }
            }
        };
        this.propsObs.on('set', (data) => {
            data.key === 'value' && (this.valMap = { newVal: data.value, oldVal: data.raw || '' });
        });
        this.propsObs.on('completed', (data) => {
            if (data.keys.set.includes('value')) {
                this.updateCache({ check: data.proxy.value });
                this.listen({ name: 'changed', params: [this.valMap] });
            }
        });
    }
    static custAttrs = ['size', 'name', 'type', 'layout', 'cols', 'checked', 'disable', 'content', 'wrap-classes', 'item-classes', 'input-classes', 'on-checked', ...this.evtsArr];
    static boolAttrs = ['disabled'];
    static get observedAttributes() {
        return ['content', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.saveProps(name, newVal, RadiosElem);
        if (name === 'content') {
            this.updateInputs(newVal);
        }
        else if (name === 'disabled') {
            this.toggleAttribute('disabled', this.propsProxy['disabled']);
            this.disabled = this.propsProxy['disabled'];
        }
        else if (['name', 'type', 'size'].includes(name)) {
            this.inputs.forEach((k) => {
                newVal ? k.setAttribute(name, newVal) : k.removeAttribute(name);
            });
            if (name === 'name') {
                newVal ? this.name = newVal : this.removeAttribute('name');
            }
        }
        else if (name === 'checked') {
            if (newVal === null) {
                
                this.inputs.forEach((k) => {
                    k.check('');
                });
            }
            else {
                
                this.inputs.forEach((k) => {
                    if (k.propsProxy.value === newVal) {
                        k.check('ed');
                    }
                    else {
                        k.check('');
                    }
                });
            }
        }
        else if (name === 'disable') {
            let value = getAttrArr(newVal);
            value.forEach((k) => {
                let tmp = this.inputs.find((i) => i.propsProxy.value === k);
                tmp && tmp.setAttribute('disabled', '');
            });
        }
    }
    connectedCallback() {
        this.connectedRender();
    }
    getRawData() {
        this.getRawProps(RadiosElem);
        !this.propsRaw.name && (this.propsRaw.name = ax.namePfx + Date.now());
        this.propsRaw.content = super.getItemsData();
        this.getProxyProps();
        this.setFieldProps(['name', 'value', 'disabled']);
    }
    fillWrap(data) {
        this.wrapEl = createEl('div', { [alias]: 'wrap' });
        data['wrap-classes'] && classes(this.wrapEl).add(data['wrap-classes']);
        if (data.layout === 'grid') {
            this.wrapEl.classList.add(`${prefix}grid`);
            data.cols && this.wrapEl.classList.add(`${prefix}avg-${data.cols}`);
        }
        else if (data.layout === 'flex') {
            this.wrapEl.classList.add(`${prefix}row`);
        }
        this.updateInputs(data.content, data);
    }
    updateInputs(data, host = this.propsProxy) {
        this.inputs = [];
        this.wrapEl.innerHTML = '';
        let tmp = super.getItemsData(data);
        for (let k of tmp) {
            let parent = createEl('div'), input = createEl('ax-radio', { label: k.label, value: isNull(k.value) ? k.label : k.value, [ax.embedSign]: '' });
            host.name ? input.setAttribute('name', host.name) : input.removeAttribute('name');
            host.type ? input.setAttribute('type', host.type) : input.removeAttribute('type');
            k.checked ? input.setAttribute('check', 'ed') : input.removeAttribute('check');
            input.toggleAttribute('disabled', !!k.disabled);
            host['input-classes'] && classes(input).add(host['input-classes']);
            this.inputs.push(input);
            if (host.layout) {
                host.layout === 'flex' && parent.classList.add(`${prefix}flex`);
                host['item-classes'] && classes(parent).add(host['item-classes']);
                parent.appendChild(input);
                this.wrapEl.appendChild(parent);
            }
            else {
                this.wrapEl.appendChild(input);
            }
            input.on('changed', (data) => {
                let eds = this.inputs.find((i) => i.propsProxy.check === 'ed');
                this.propsProxy.value = eds ? eds.propsProxy.value : '';
                this.value = this.propsProxy.value;
                data.newVal && this.listen({ name: 'checked', params: [input] });
            });
        }
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
}

class CheckboxesElem extends CompBaseCommField {
    labelEl;
    legendEl;
    inputs;
    checkedAll;
    switch;
    setVals;
    check;
    setLabels;
    switchType;
    switchEvent;
    valMap;
    checkedMore;
    checkedLess;
    type;
    constructor() {
        super();
        this.type = 'checkboxes-comp';
        this.getRawData();
        this.fillWrap(this.propsProxy);
        this.reset = () => {
            if (this.propsRaw.content != this.propsRaw.content)
                this.updateInputs(this.propsRaw.content, this.propsRaw);
            for (let k in this.propsRaw) {
                if (k === 'content')
                    continue;
                (this.propsRaw[k] !== null && this.propsRaw[k] !== false) ? this.setAttribute(k, this.propsRaw[k]) : this.removeAttribute(k);
            }
            this.listen({ name: 'reset' });
        };
        this.clear = () => {
            let tmp = this.inputs.every((k) => !k.propsProxy.check);
            if (tmp)
                return;
            this.setAttribute('checked', '');
            this.listen({ name: 'cleared' });
        };
        this.checkedAll = () => {
            let vals = this.inputs.map((k) => !k.propsProxy.checked && k.propsProxy.value);
            if (!vals.length)
                return;
            this.setAttribute('checked', vals);
            this.listen({ name: 'checkedAll', params: [vals] });
        };
        this.checkedMore = (data) => {
            let vals = valToArr(data), cur = this.value.split(','), value = unique([...cur, ...vals]).filter(Boolean);
            this.setAttribute('checked', value.join(','));
        };
        this.checkedLess = (data) => {
            let vals = valToArr(data), cur = this.value.split(','), value = cur.filter((k) => !vals.includes(k));
            this.setAttribute('checked', value.join(','));
        };
        this.check = (value, type = 'ed', from = 'value') => {
            let vals = valToArr(value);
            if (!vals.length) {
                this.setAttribute('checked', '');
                return;
            }
            let inputs = vals.map((k) => this.inputs.find((i) => i.propsProxy[from] == k)).filter(Boolean);
            if (!inputs.length)
                return;
            let val = inputs.map((k) => k.value), cur = this.propsProxy.value.split(','), diff = !type ? cur.filter((k) => !val.includes(k)) : val;
            if (type === 'ing') {
                inputs[0].checked && this.setAttribute('checked', cur.shift.join(','));
                inputs[0].setAttribute('check', type);
            }
            else {
                this.setAttribute('checked', diff.join(','));
            }
        };
        this.propsObs.on('set', (data) => {
            data.key === 'value' && (this.valMap = { newVal: data.value, oldVal: data.raw || '' });
        });
        this.propsObs.on('completed', (data) => {
            if (data.keys.set.includes('value')) {
                this.referSwitch(data.proxy.value);
                this.updateCache({ check: data.proxy.value });
                this.listen({ name: 'changed', params: [this.valMap] });
            }
        });
        this.switchEvent = (e) => {
            this.switch.value == '1' ? this.checkedAll() : this.clear();
        };
    }
    static custAttrs = [
        'size', 'name', 'type', 'layout', 'cols', 'checked', 'disable', 'content', 'switch', 'wrap-classes', 'item-classes', 'input-classes', 'on-checkedall', ...this.evtsArr
    ];
    static boolAttrs = ['disabled'];
    static get observedAttributes() {
        return ['content', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.saveProps(name, newVal, CheckboxesElem);
        if (name === 'content') {
            this.updateInputs(newVal);
        }
        else if (name === 'disabled') {
            this.toggleAttribute('disabled', this.propsProxy['disabled']);
            this.disabled = this.propsProxy['disabled'];
        }
        else if (['name', 'type', 'size'].includes(name)) {
            this.inputs.forEach((k) => {
                newVal ? k.setAttribute(name, name === 'name' ? `${newVal}[]` : newVal) : k.removeAttribute(name);
            });
            if (name === 'name') {
                newVal ? this.name = newVal : this.removeAttribute('name');
            }
        }
        else if (name === 'checked') {
            if (newVal === null) {
                this.inputs.forEach((k) => {
                    k.check('');
                });
            }
            else {
                let tmp = !newVal ? [''] : getAttrArr(newVal);
                this.inputs.forEach((k) => {
                    if (tmp.includes(k.propsProxy.value)) {
                        k.check('ed');
                    }
                    else {
                        k.check('');
                    }
                });
            }
        }
        else if (name === 'disable') {
            let tmp = getAttrArr(newVal);
            this.inputs.forEach((k) => {
                if (tmp.includes(k.propsProxy.value)) {
                    k.setAttribute('disabled', '');
                }
                else {
                    k.removeAttribute('disabled');
                }
            });
        }
        else if (name === 'switch') {
            this.switch = getEl(`[name="${newVal}"]`);
            if (this.switch) {
                this.switchType = this.switch.nodeName;
                for (let k of this.switch.inputs) {
                    this.switch.removeEventListener('change', this.switchEvent);
                    this.switch.addEventListener('change', this.switchEvent, false);
                }
            }
        }
    }
    connectedCallback() {
        this.connectedRender();
    }
    getRawData() {
        this.getRawProps(CheckboxesElem);
        !this.propsRaw.name && (this.propsRaw.name = ax.namePfx + Date.now());
        this.propsRaw.content = super.getItemsData();
        this.getProxyProps();
        this.setFieldProps(['name', 'value', 'disabled']);
    }
    fillWrap(data) {
        this.wrapEl = createEl('div', { [alias]: 'wrap' });
        data['wrap-classes'] && classes(this.wrapEl).add(data['wrap-classes']);
        if (data.layout === 'grid') {
            this.wrapEl.classList.add(`${prefix}grid`);
            data.cols && this.wrapEl.classList.add(`${prefix}avg-${data.cols}`);
        }
        else if (data.layout === 'flex') {
            this.wrapEl.classList.add(`${prefix}row`);
        }
        this.updateInputs(data.content, data);
    }
    updateInputs(data, host = this.propsProxy) {
        this.inputs = [];
        this.wrapEl.innerHTML = '';
        let tmp = super.getItemsData(data);
        for (let k of tmp) {
            let parent = createEl('div'), input = createEl('ax-checkbox', { label: k.label, value: isNull(k.value) ? k.label : k.value, [ax.embedSign]: '' });
            host.name ? input.setAttribute('name', `${host.name}[]`) : input.removeAttribute('name');
            host.type ? input.setAttribute('type', host.type) : input.removeAttribute('type');
            k.checked ? input.setAttribute('check', 'ed') : input.removeAttribute('check');
            input.toggleAttribute('disabled', !!k.disabled);
            host['input-classes'] && classes(input).add(host['input-classes']);
            this.inputs.push(input);
            if (host.layout) {
                host.layout === 'flex' && parent.classList.add(`${prefix}flex`);
                host['item-classes'] && classes(parent).add(host['item-classes']);
                parent.appendChild(input);
                this.wrapEl.appendChild(parent);
            }
            else {
                this.wrapEl.appendChild(input);
            }
        }
        this.inputs.forEach((k) => {
            k.on('changed', () => {
                let eds = this.inputs.filter((i) => i.propsProxy.check === 'ed').map((o) => o.propsProxy.value).join(',');
                this.propsProxy.value = eds;
                this.value = this.propsProxy.value;
            });
        });
    }
    referSwitch(val) {
        if (!this.switch)
            return;
        if (!val) {
            this.switchType === 'AX-CHECKBOXES' ? this.switch.clear() : this.switch.check('0');
        }
        else {
            let tmp = val.split(',').length;
            if (tmp > 0 && tmp < this.inputs.length) {
                this.switch.clear();
                
                let tmp = this.switch.inputs.find((k) => k.propsProxy.value == '1');
                tmp && tmp.setAttribute('check', 'ing');
            }
            else {
                
                this.switch.check('1');
            }
        }
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
}

class NumberElem extends CompBaseCommField {
    labelEl;
    inputEvt;
    tipsEl;
    incrEl;
    decrEl;
    type;
    select;
    constructor() {
        super();
        this.type = 'number-comp';
        this.getRawData();
        this.fillWrap(this.propsProxy);
        this.select = () => this.inputEl.select();
        this.on('input', (value) => {
            let step = ~~this.propsProxy.step || 1, val = ~~value, max = ~~this.propsProxy.max, min = ~~this.propsProxy.min;
            if (!isNull(this.propsProxy.max)) {
                if (val + step > max) {
                    this.incrEl.toggleAttribute('disabled', true);
                }
                else {
                    this.incrEl.removeAttribute('disabled');
                }
            }
            if (!isNull(this.propsProxy.min)) {
                if (val - step < min) {
                    this.decrEl.toggleAttribute('disabled', true);
                }
                else {
                    this.decrEl.removeAttribute('disabled');
                }
            }
        });
    }
    static custAttrs = ['name', 'placeholder', 'layout', 'size', 'max', 'min', 'step', 'label', 'on-exceeded', ...this.evtsArr];
    static boolAttrs = ['tips', 'disabled', 'readonly', 'full'];
    static get observedAttributes() {
        return ['value', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.saveProps(name, newVal, NumberElem);
        if (NumberElem.boolAttrs.includes(name)) {
            this.inputEl[name === 'readonly' ? 'readOnly' : name] = this.propsProxy[name];
            this[name === 'readonly' ? 'readOnly' : name] = this.propsProxy[name];
            if (name === 'tips') {
                if (this.propsProxy.tips) {
                    elState(this.tipsEl).isVirtual && this.appendChild(this.tipsEl);
                }
                else {
                    this.tipsEl.remove();
                }
            }
        }
        else if (['value', 'max', 'min', 'step'].includes(name)) {
            this.inputEl[name] = newVal;
            this.value = newVal;
            (name === 'max' || name === 'min') && this.updateTips();
        }
        else if (['name', 'placeholder'].includes(name)) {
            this.inputEl.setAttribute(name, newVal);
            name === 'name' && (this.name = newVal);
        }
        else if (name === 'label') {
            if (newVal) {
                this.labelEl.innerHTML = newVal;
                elState(this.labelEl).isVirtual && this.wrapEl.insertAdjacentElement('afterbegin', this.labelEl);
                this.setAttribute('layout', 'embed');
            }
            else {
                this.labelEl.remove();
            }
        }
        if (oldVal !== newVal) {
            this.listen({ name: 'changed', params: [{ oldVal, newVal }] });
        }
    }
    getRawData() {
        this.getRawProps(NumberElem);
        this.propsRaw.value = parseFloat(this.getAttribute('value') || this.getConnCont('text'));
        this.getProxyProps();
        this.setFieldProps(['name', 'value', 'disabled', 'readOnly']);
    }
    fillWrap(data) {
        this.wrapEl = createEl('div', { [alias]: 'wrap' });
        this.inputEl = createEl('input', { type: 'number', name: data.name, placeholder: data.placeholder || config.lang.form.placeholder, [ax.embedSign]: '' });
        data.value && (this.inputEl.value = data.value);
        this.decrEl = createEl('i', { [alias]: 'decrease' }, 'minus');
        this.incrEl = createEl('i', { [alias]: 'increase' }, 'plus');
        this.labelEl = createEl('span', { [alias]: 'label' });
        this.wrapEl.append(this.decrEl, this.inputEl, this.incrEl);
        this.tipsEl = createEl('div', { [alias]: 'tips' });
    }
    updateTips() {
        let max = isNull(this.propsProxy.max) ? '+∞' : this.propsProxy.max, min = isNull(this.propsProxy.min) ? '-∞' : this.propsProxy.min, val = this.propsProxy.value, old = ~~val, str = '';
        if (max === '+∞' && min === '-∞') {
            return this;
        }
        if (max !== '+∞' && min !== '-∞') {
            str = renderTpl(this.propsProxy.lang?.limit || config.lang.form.limitNumber, { max, min });
            if (~~val >= ~~min && ~~val <= ~~max) {
                this.removeAttribute('exceeded');
                this.decrEl.removeAttribute('disabled');
                this.incrEl.removeAttribute('disabled');
            }
            else {
                this.setAttribute('exceeded', '');
                str += (this.propsProxy.lang?.exceed || config.lang.form.exceed);
                val = (~~val < ~~min) ? min : (~~val > ~~max) ? max : val;
                ~~val > old ? this.decrEl.setAttribute('disabled', '') : this.incrEl.setAttribute('disabled', '');
                this.listen({ name: 'exceeded', params: [val, old] });
            }
        }
        else if (max !== '+∞') {
            str = renderTpl(this.propsProxy.lang?.max || config.lang.form.maxNumber, { max });
            if (~~val > ~~max) {
                val = max;
                this.setAttribute('exceeded', '');
                str += (this.propsProxy.lang?.exceed || config.lang.form.exceed);
                this.incrEl.setAttribute('disabled', '');
                this.listen({ name: 'exceeded', params: [val, old] });
            }
            else {
                this.removeAttribute('exceeded');
                this.incrEl.removeAttribute('disabled');
            }
        }
        else if (min !== '-∞') {
            str = renderTpl(this.propsProxy.lang?.min || config.lang.form.minNumber, { min });
            if (~~val < ~~min) {
                val = min;
                this.setAttribute('exceeded', '');
                str += (this.propsProxy.lang?.exceed || config.lang.form.exceed);
                this.decrEl.setAttribute('disabled', '');
                this.listen({ name: 'exceeded', params: [val, old] });
            }
            else {
                this.removeAttribute('exceeded');
                this.decrEl.removeAttribute('disabled');
            }
        }
        if (val !== this.propsProxy.value) {
            this.setAttribute('value', val);
        }
        this.tipsEl.innerHTML = str;
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
        (this.propsProxy.tips && (this.propsProxy.max || this.propsProxy.min) && elState(this.tipsEl).isVirtual) ? this.appendChild(this.tipsEl) : null;
        let evt = new Event('input');
        this.decrEl.onclick = () => {
            this.inputEl.stepDown();
            this.inputEl.dispatchEvent(evt);
        };
        this.incrEl.onclick = () => {
            this.inputEl.stepUp();
            this.inputEl.dispatchEvent(evt);
        };
        this.inputEl.addEventListener('input', (e) => {
            this.propsProxy.value = this.inputEl.value;
            this.value = this.inputEl.value;
            this.updateTips();
            this.listen({ name: 'input', params: [this.inputEl.value] });
        }, false);
    }
}

class RangeElem extends CompBaseCommFieldMixin {
    type;
    constructor() {
        super();
        this.type = 'range-comp';
        this.getRawData();
    }
    static custAttrs = ['name', 'step', 'max', 'min', 'axis', 'size', 'classes', 'separator', 'hyphen', 'fence', 'button', 'ruler', 'result'];
    static boolAttrs = ['disabled', 'full', 'limit-show', 'tip-show', 'multiple', 'locked'];
    
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.propsProxy[name] = this.mapVals(name, newVal, RangeElem, optRange);
        !RangeElem.evtsArr.includes(name) && this.saveModsOpts(name, optRange);
        if (name === 'name') {
            let tmp = newVal || '';
            this.ins && (this.ins.inputEl.name = tmp);
            this.name = tmp;
        }
        else if (name === 'value') {
            this.ins && this.ins.setVals(newVal);
            this.value = this.ins ? this.ins.output.value : newVal;
        }
        else if (name === 'disabled') {
            this.disabled = this.propsProxy[name];
            if (this.ins) {
                this.disabled ? this.ins.disable() : this.ins.enable();
            }
        }
        else if (name === 'size') {
            if (this.ins) {
                newVal ? this.ins.targetEl.setAttribute('size', newVal) : this.ins.targetEl.removeAttribute('size');
            }
        }
    }
    getRawData() {
        for (let k of [...RangeElem.custAttrs, ...RangeElem.lazyAttrs, ...RangeElem.boolAttrs, ...RangeElem.evtsArr]) {
            this.propsRaw[k] = this.mapVals(k, this.getAttribute(k), RangeElem, optRange);
        }
        this.getProxyProps();
        for (let k of Object.keys(this.properties).filter(keyCond))
            this.saveModsOpts(k, optRange);
        this.setFieldProps(['name', 'value', 'disabled']);
    }
    newIns() {
        let params = Object.assign({
            onSet: (data) => {
                this.modsOpts['module'].value = this.propsProxy.value = this.value = data.value;
                this.canListen = false;
                this.setAttribute('value', data.value);
                this.canListen = true;
                this.dispatchEvent(this.events['input'].event);
                this.listen({ name: 'input', params: [data.value] });
                if (data.value !== data.raw) {
                    this.dispatchEvent(this.events['change'].event);
                    this.listen({ name: 'changed', params: [{ newVal: data.value, oldVal: data.raw }] });
                }
            },
        }, deepClone(this.modsOpts['module']));
        this.ins = new Range(this.wrapEl, params);
        this.ins.inputEl.setAttribute(ax.embedSign, '');
        this.focus = () => {
            this.ins.handles[this.propsProxy.multiple ? 'from' : 'single'].focus();
        };
        this.blur = () => {
            if (this.propsProxy.multiple) {
                this.ins.handles.from.blur();
                this.ins.handles.to.blur();
            }
            else {
                this.ins.handles.single.blur();
            }
        };
        this.clear = () => {
            this.ins.clear();
            this.listen({ name: 'cleared' });
        };
        for (let k in this.ins.handles) {
            if (this.ins.handles[k]) {
                this.ins.handles[k].addEventListener('focus', () => {
                    this.dispatchEvent(this.events['focus'].event);
                }, false);
                this.ins.handles[k].addEventListener('blur', () => {
                    this.dispatchEvent(this.events['blur'].event);
                }, false);
            }
        }
    }
    render() {
        this.insertSource();
        this.wrapEl = createEl('div', {}, this.rawHtml);
        this.appendChild(this.wrapEl);
        this.propsObs.on('completed', (data) => {
            let keyArr = [...RangeElem.custAttrs, ...RangeElem.boolAttrs].filter((k) => !['name', 'disabled', 'value', 'size'].includes(k)), intArr = getIntArr([keyArr, data.keys.set]);
            intArr.length && this.ins && this.ins.update(this.modsOpts['module']);
            intArr.length && (this.connCount <= 1 ? this.newIns() : this.ins.update(this.modsOpts['module']));
        });
    }
}

class StatsElem extends CompBaseComm {
    mainEl;
    badgeEl;
    tipsEl;
    labelEl;
    unitEl;
    iconEl;
    cubeEl;
    diskEl;
    headEl;
    imageEl;
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['unit', 'tips', 'icon', 'disk', 'cube', 'image', 'badge', 'dir', ...this.baseAttrs];
    static boolAttrs = ['inverted', 'disabled'];
    static get observedAttributes() {
        return ['label', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, StatsElem);
    }
    getRawData() {
        this.getRawProps(StatsElem);
        this.propsRaw.label = this.getAttribute('label') || this.rawHtml;
        this.getProxyProps();
    }
    fillWrap(data) {
        this.wrapEl = createEl('div', { [alias]: 'wrap' });
        this.mainEl = createEl('div', { [alias]: 'main' });
        this.iconEl = createEl('i', { [alias]: 'icon', class: data.icon });
        this.diskEl = createEl('span', { [alias]: 'disk' }, `<img src="${data.disk}"/>`);
        this.cubeEl = createEl('span', { [alias]: 'cube' }, `<img src="${data.cube}"/>`);
        this.imageEl = createEl('span', { [alias]: 'image' }, `<img src="${data.image}"/>`);
        this.labelEl = createEl('i', { [alias]: 'label' }, data.label);
        this.headEl = createEl('div', { [alias]: 'head' }, this.labelEl);
        this.tipsEl = createEl('div', { [alias]: 'tips' }, data.tips);
        this.unitEl = createEl('i', { [alias]: 'unit' }, data.unit);
        this.badgeEl = createEl('ax-badge', { [alias]: 'badge', label: data?.badge?.trim() });
        this.mainEl.appendChild(this.headEl);
        this.wrapEl.appendChild(this.mainEl);
    }
    render() {
        this.insertSource();
        !this.hasAttribute('dir') && this.setAttribute('dir', 'v');
        this.append(this.wrapEl);
    }
    changedMaps = {
        badge: this.changedBadge,
        label: this.changedLabel,
        icon: this.changedIcon,
        disk: this.changedDisk,
        cube: this.changedCube,
        image: this.changedImage,
        tips: this.changedTips,
        unit: this.changedUnit,
    };
    changedBadge(opt) {
        if (opt.newVal === null) {
            this.badgeEl.remove();
        }
        else {
            this.badgeEl.setAttribute('label', opt.newVal.trim());
            elState(this.badgeEl).isVirtual && this.labelEl.appendChild(this.badgeEl);
        }
    }
    changedIcon(opt) {
        if (opt.newVal === null) {
            this.iconEl.remove();
        }
        else {
            this.iconEl.setAttribute('class', opt.newVal);
            if (elState(this.iconEl).isVirtual) {
                this.diskEl.remove();
                this.cubeEl.remove();
                this.imageEl.remove();
                this.wrapEl.insertAdjacentElement('afterbegin', this.iconEl);
            }
        }
    }
    changedDisk(opt) {
        if (opt.newVal === null) {
            this.diskEl.remove();
        }
        else {
            this.diskEl.querySelector('img').src = opt.newVal;
            if (elState(this.diskEl).isVirtual) {
                this.iconEl.remove();
                this.cubeEl.remove();
                this.imageEl.remove();
                this.wrapEl.insertAdjacentElement('afterbegin', this.diskEl);
            }
        }
    }
    changedCube(opt) {
        if (opt.newVal === null) {
            this.cubeEl.remove();
        }
        else {
            this.cubeEl.querySelector('img').src = opt.newVal;
            if (elState(this.cubeEl).isVirtual) {
                this.diskEl.remove();
                this.imageEl.remove();
                this.iconEl.remove();
                this.wrapEl.insertAdjacentElement('afterbegin', this.cubeEl);
            }
        }
    }
    changedImage(opt) {
        if (opt.newVal === null) {
            this.imageEl.remove();
        }
        else {
            this.imageEl.querySelector('img').src = opt.newVal;
            if (elState(this.cubeEl).isVirtual) {
                this.diskEl.remove();
                this.cubeEl.remove();
                this.iconEl.remove();
                this.wrapEl.insertAdjacentElement('afterbegin', this.imageEl);
            }
        }
    }
    changedTips(opt) {
        if (opt.newVal === null) {
            this.tipsEl.remove();
        }
        else {
            this.tipsEl.innerHTML = opt.newVal;
            this.mainEl.appendChild(this.tipsEl);
        }
    }
    changedUnit(opt) {
        if (opt.newVal === null) {
            this.unitEl.remove();
        }
        else {
            this.unitEl.innerHTML = opt.newVal;
            this.headEl.appendChild(this.unitEl);
        }
    }
    changedLabel(opt) {
        this.labelEl.innerHTML = opt.newVal || '';
    }
}

class IconElem extends CompBaseComm {
    badgeEl;
    tipsEl;
    iconEl;
    cubeEl;
    diskEl;
    imageEl;
    headEl;
    legendEl;
    tmp;
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['tips', 'type', 'badge', 'dir', 'bg', 'href', 'target', 'rel', 'download', ...this.baseAttrs];
    static boolAttrs = ['disabled'];
    static get observedAttributes() {
        return ['label', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, IconElem);
    }
    getRawData() {
        this.getRawProps(IconElem);
        this.propsRaw.label = this.getAttribute('label') || this.rawHtml;
        this.getProxyProps();
    }
    fillWrap(data) {
        this.headEl = createEl('span', { [alias]: 'head' });
        this.wrapEl = createEl(data.href ? 'a' : 'div', { [alias]: 'wrap' }, this.headEl);
        this.iconEl = createEl('i', { [alias]: 'icon', class: data.label });
        this.diskEl = createEl('span', { [alias]: 'disk' }, `<img src="${data.label}"/>`);
        this.cubeEl = createEl('span', { [alias]: 'cube' }, `<img src="${data.label}"/>`);
        this.imageEl = createEl('span', { [alias]: 'image' }, `<img src="${data.label}"/>`);
        this.tipsEl = createEl('div', { [alias]: 'tips' }, data.tips);
        this.toggleLegend('icon');
        this.badgeEl = createEl('ax-badge', { [alias]: 'badge', label: data?.badge?.trim() });
    }
    setLegendBg(theme, old) {
        if (theme) {
            classes(this.iconEl).replace(`${prefix}bg-${old}`, `${prefix}bg-${theme}`);
            classes(this.diskEl).replace(`${prefix}bg-${old}`, `${prefix}bg-${theme}`);
            classes(this.cubeEl).replace(`${prefix}bg-${old}`, `${prefix}bg-${theme}`);
        }
        else {
            this.iconEl.classList.remove(`${prefix}bg-${old}`);
            this.diskEl.classList.remove(`${prefix}bg-${old}`);
            this.cubeEl.classList.remove(`${prefix}bg-${old}`);
        }
    }
    toggleLegend(type) {
        let map = [{
                type: 'icon',
                show: this.iconEl,
                hide: [this.diskEl, this.imageEl, this.cubeEl],
            }, {
                type: 'disk',
                show: this.diskEl,
                hide: [this.iconEl, this.imageEl, this.cubeEl],
            }, {
                type: 'cube',
                show: this.cubeEl,
                hide: [this.diskEl, this.imageEl, this.iconEl],
            }, {
                type: 'image',
                show: this.imageEl,
                hide: [this.diskEl, this.cubeEl, this.iconEl],
            }];
        let item = map.find((k) => k.type === type);
        if (item) {
            for (let k of item.hide)
                k.remove();
            elState(item.show).isVirtual && this.headEl.insertAdjacentElement('afterbegin', item.show);
            this.legendEl = item.show;
        }
        else {
            this.iconEl.remove();
            this.cubeEl.remove();
            this.diskEl.remove();
            this.imageEl.remove();
        }
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
        !this.hasAttribute('dir') && this.setAttribute('dir', 'h');
        if (!this.hasAttribute('type')) {
            this.canListen = false;
            this.setAttribute('type', 'icon');
            this.canListen = true;
        }
    }
    changedMaps = {
        label: this.changedLabel,
        href: this.changedHref,
        badge: this.changedBadge,
        target: this.changedTarget,
        rel: this.changedTarget,
        download: this.changedTarget,
        tips: this.changedTips,
        type: this.changedType,
        bg: this.changedBg,
    };
    changedLabel(opt) {
        if (opt.newVal === null) {
            this.toggleLegend();
        }
        else {
            if (this.legendEl.nodeName === 'I') {
                this.legendEl.setAttribute('class', opt.newVal);
            }
            else {
                this.legendEl.querySelector('img').src = opt.newVal;
            }
        }
    }
    changedBadge(opt) {
        if (opt.newVal === null) {
            this.badgeEl.remove();
        }
        else {
            this.badgeEl.setAttribute('label', opt.newVal.trim());
            elState(this.badgeEl).isVirtual && this.headEl.appendChild(this.badgeEl);
        }
    }
    changedHref(opt) {
        if (opt.newVal === null) {
            let children = this.wrapEl.children;
            this.wrapEl = createEl('div', { [alias]: 'wrap' });
            this.wrapEl.append(...children);
            this.render();
        }
        else {
            if (this.wrapEl.nodeName !== 'A') {
                this.wrapEl = createEl('a', { [alias]: 'wrap' }, [...this.wrapEl.children]);
                this.render();
            }
            this.wrapEl.setAttribute('href', opt.newVal);
        }
    }
    changedTarget(opt) {
        opt.newVal ? this.wrapEl[opt.name] = opt.newVal : this.wrapEl.removeAttribute(opt.name);
    }
    changedTips(opt) {
        if (opt.newVal === null) {
            this.tipsEl.remove();
        }
        else {
            this.tipsEl.innerHTML = opt.newVal;
            this.wrapEl.appendChild(this.tipsEl);
        }
    }
    changedBg(opt) {
        this.setLegendBg(opt.newVal, opt.oldVal);
    }
    changedType(opt) {
        this.toggleLegend(opt.newVal);
    }
}

class BadgeElem extends CompBaseComm {
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['theme', 'type', 'shape', ...this.baseAttrs];
    static boolAttrs = ['glassy'];
    static get observedAttributes() {
        return ['label', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, BadgeElem);
    }
    getRawData() {
        this.getRawProps(BadgeElem);
        this.propsRaw.label = this.getAttribute('label') || this.rawHtml;
        this.getProxyProps();
    }
    fillWrap(data) {
        this.wrapEl = createEl('div', { [alias]: 'wrap' }, data.label);
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
    changedMaps = {
        label: this.changedLabel,
    };
    changedLabel(opt) {
        if (opt.newVal === null) {
            this.wrapEl.innerHTML = '';
        }
        else {
            this.wrapEl.innerHTML = opt.newVal;
        }
    }
}

class DividerElem extends CompBaseComm {
    startEl;
    endEl;
    labelEl;
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['break', 'size', 'fs', ...this.baseAttrs];
    static boolAttrs = [];
    static get observedAttributes() {
        return ['label', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, DividerElem);
    }
    getRawData() {
        this.getRawProps(DividerElem);
        this.propsRaw.label = this.getAttribute('label') || this.rawHtml;
        this.getProxyProps();
    }
    fillWrap(data) {
        this.startEl = createEl('div', { [alias]: 'start' });
        this.wrapEl = createEl('div', { [alias]: 'wrap' }, this.startEl);
        this.endEl = createEl('div', { [alias]: 'end' });
        this.labelEl = createEl('div', { [alias]: 'label' }, this.propsProxy.label);
        this.propsProxy.label && this.wrapEl.append(this.labelEl, this.endEl);
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
    changedMaps = {
        label: this.changedLabel,
    };
    changedLabel(opt) {
        if (opt.newVal === null) {
            this.labelEl.remove();
            this.endEl.remove();
        }
        else {
            this.labelEl.innerHTML = opt.newVal;
            elState(this.labelEl).isVirtual && this.wrapEl.append(this.labelEl, this.endEl);
        }
    }
}

class AlarmElem extends CompBaseComm {
    labelEl;
    iconEl;
    imageEl;
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['icon', 'image', 'theme', 'size', ...this.baseAttrs];
    static boolAttrs = [];
    static get observedAttributes() {
        return ['label', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, AlarmElem);
    }
    getRawData() {
        this.getRawProps(AlarmElem);
        this.propsRaw.label = this.getAttribute('label') || this.rawHtml;
        this.getProxyProps();
    }
    fillWrap(data) {
        this.wrapEl = createEl('span', { [alias]: 'wrap' });
        this.labelEl = createEl('i', { [alias]: 'label' }, this.propsProxy.label);
        this.iconEl = createEl('i', { [alias]: 'icon' });
        this.imageEl = createEl('img', { [alias]: 'image' });
        this.propsProxy.label && this.wrapEl.appendChild(this.labelEl);
    }
    render(data) {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
    changedMaps = {
        label: this.changedLabel,
        icon: this.changedIcon,
        image: this.changedImage,
    };
    changedLabel(opt) {
        if (opt.newVal === null) {
            this.labelEl.remove();
        }
        else {
            this.labelEl.innerHTML = opt.newVal;
            if (elState(this.labelEl).isVirtual) {
                this.wrapEl.appendChild(this.labelEl);
                this.iconEl.remove();
                this.imageEl.remove();
            }
        }
    }
    changedIcon(opt) {
        if (opt.newVal === null) {
            this.iconEl.remove();
        }
        else {
            this.iconEl.setAttribute('class', opt.newVal);
            if (elState(this.iconEl).isVirtual) {
                this.wrapEl.appendChild(this.iconEl);
                this.labelEl.remove();
                this.imageEl.remove();
            }
        }
    }
    changedImage(opt) {
        if (opt.newVal === null) {
            this.imageEl.remove();
        }
        else {
            this.imageEl.src = opt.newVal;
            if (elState(this.imageEl).isVirtual) {
                this.wrapEl.appendChild(this.imageEl);
                this.labelEl.remove();
                this.iconEl.remove();
            }
        }
    }
}

class ProgressElem extends CompBaseComm {
    constructor() {
        super();
        this.createShadow();
        super.createPropsObs();
    }
    static get observedAttributes() {
        return [...optProgress.map((k) => k.attr), 'options'];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (name === 'value') {
            if (!this.connected)
                return;
            let duration = this.getAttribute('duration'), value = this.getVal(newVal);
            if (duration == 0) {
                this.ins.locateTo(value);
            }
            else {
                !duration ? this.ins.animateTo(value) : this.ins.animateTo(value, { duration });
            }
        }
        else {
            super.updateProxy(name, newVal, optProgress);
        }
    }
    render() {
        this.insertSource();
        this.wrapEl = createEl('div', { [alias]: 'slot-host' }, this.rawHtml);
        this.appendChild(this.wrapEl);
        this.ins = new Progress(this.wrapEl);
        setTimeout(() => {
            this.propsObs.on('completed', (data) => {
                this.ins.update(this.properties);
                this.listen({ name: 'updated', params: [this.properties] });
            });
        }, 500);
    }
    getVal(val) {
        return val == 0 ? 0 : ~~val === 0 ? val : parseFloat(val);
    }
}

class DatetimeElem extends CompBaseCommFieldMixin {
    labelEl;
    inputEvt;
    meanEl;
    datetimeIns;
    type;
    select;
    constructor() {
        super();
        this.type = 'datetime-comp';
        this.getRawData();
        this.select = () => this.ins.inputEl.select();
    }
    static custAttrs = ['name', 'value', 'size', 'format', 'classes', 'mode', 'feature', 'display', 'placeholder', 'label', 'tools',
        'max-selection', 'min-date', 'max-date', 'datespan', 'timespan', 'rows', 'cols', 'week-start', 'separator', 'btn-sel', 'pos-sel', 'input-sel', 'child-sel', 'lunar', 'events', 'menu', 'bubble', 'footer'];
    static boolAttrs = ['disabled', 'to-drawer', 'full', 'multiline', 'now-hide', 'now-show', 'clear-show', 'close-show', 'cancel-show', 'confirm-hide', 'auto-fill', 'manual', 'auto-correct', 'required', 'fill-now'];
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.propsProxy[name] = this.mapVals(name, newVal, DatetimeElem, optDatetime);
        !DatetimeElem.evtsArr.includes(name) && this.saveModsOpts(name, optDatetime);
        this.changedMaps[name] && this.changedMaps[name].call(this, { name, newVal, oldVal, proxy: this.propsProxy });
    }
    getRawData() {
        for (let k of [...DatetimeElem.custAttrs, ...DatetimeElem.lazyAttrs, ...DatetimeElem.boolAttrs, ...DatetimeElem.evtsArr]) {
            this.propsRaw[k] = this.mapVals(k, this.getAttribute(k), DatetimeElem, optDatetime);
        }
        this.getProxyProps();
        for (let k of Object.keys(this.properties).filter(keyCond))
            this.saveModsOpts(k, optDatetime);
        this.setFieldProps(['name', 'value', 'disabled', 'readOnly']);
    }
    newIns() {
        let params = Object.assign(this.modsOpts['module'], {
            onOutput: (data) => {
                this.modsOpts['module'].value = this.propsProxy.value = this.value = data.value;
                this.canListen = false;
                this.setAttribute('value', data.value);
                this.canListen = true;
                this.dispatchEvent(this.events['input'].event);
                this.listen({ name: 'input', params: [data.value] });
                if (data.value !== data.raw) {
                    this.dispatchEvent(this.events['change'].event);
                    this.listen({ name: 'changed', params: [data] });
                }
            }
        });
        this.ins = new Datetime(this.wrapEl, params);
        this.ins.inputEl.setAttribute(ax.embedSign, '');
        this.focus = () => {
            this.ins.inputEl.focus();
        };
        this.blur = () => {
            this.ins.inputEl.blur();
        };
        this.clear = () => {
            this.ins.clear();
            this.ins.inputEl.clear();
            this.listen({ name: 'cleared' });
        };
        this.ins.inputEl.addEventListener('focus', () => {
            this.dispatchEvent(this.events['focus'].event);
        }, false);
        this.ins.inputEl.addEventListener('blur', () => {
            this.dispatchEvent(this.events['blur'].event);
        }, false);
    }
    render() {
        this.insertSource();
        this.wrapEl = createEl('div', { class: `${prefix}datetime-wrap` }, this.rawHtml);
        this.appendChild(this.wrapEl);
        this.propsObs.on('completed', (data) => {
            let keyArr = [...DatetimeElem.custAttrs, ...DatetimeElem.boolAttrs].filter((k) => !['name', 'disabled', 'value', 'size'].includes(k)), intArr = getIntArr([keyArr, data.keys.set]);
            intArr.length && (this.connCount <= 1 ? this.newIns() : this.ins.update(this.modsOpts['module']));
        });
    }
    changedMaps = {
        disabled: this.changedBool,
        size: this.changedSize,
        name: this.changedName,
        value: this.changedValue,
    };
    changedBool(opt) {
        this.disabled = this.propsProxy[opt.name];
        if (this.ins) {
            this.disabled ? this.ins.disable() : this.ins.enable();
        }
    }
    changedName(opt) {
        let tmp = opt.newVal || '';
        this.ins && (this.ins.inputEl.name = tmp);
        this.name = tmp;
    }
    changedValue(opt) {
        if (this.ins) {
            this.ins.setVals(opt.newVal);
            this.ins.confirm();
        }
        this.value = this.ins ? this.ins.output.value : opt.newVal;
    }
    changedSize(opt) {
        if (this.ins) {
            opt.newVal ? this.ins.inputEl.setAttribute('size', opt.newVal) : this.ins.inputEl.removeAttribute('size');
        }
    }
}

class RateElem extends CompBaseComm {
    constructor() {
        super();
        this.createShadow();
        super.createPropsObs();
    }
    static get observedAttributes() {
        return [...optRate.map((k) => k.attr), 'options'];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (name === 'value') {
            this.ins && newVal && this.ins.setVals(newVal);
        }
        else {
            super.updateProxy(name, newVal, optRate);
        }
    }
    render() {
        this.insertSource();
        this.wrapEl = createEl('div', { [alias]: 'slot-host' }, this.rawHtml);
        this.appendChild(this.wrapEl);
        this.ins = new Rate(this.wrapEl);
        setTimeout(() => {
            this.propsObs.on('completed', (data) => {
                this.ins.update(this.properties);
                this.listen({ name: 'updated', params: [this.properties] });
            });
        }, 500);
    }
}

class CustomElem extends HTMLElement {
    shadowEl;
    slotEl;
    wrapEl;
    ins;
    properties;
    propsObs;
    propsProxy;
    connected;
    reset;
    clear;
    timestamp;
    plans;
    constructor() {
        super();
        this[ax.compSign] = true;
        this.plans = {};
        this.timestamp = Date.now();
        this.reset = () => {
            this.ins && this.ins.reset();
        };
        this.clear = () => {
            this.ins && this.ins.clear();
        };
        this.on('connected', () => {
            this.setAttribute(ax.compSign, '');
        });
    }
    createPropsObs() {
        this.properties = {};
        this.propsObs = new Observe(this.properties);
        this.propsProxy = this.propsObs.proxy;
        this.propsObs.on('completed', () => {
            this.ins && this.ins.update(this.properties);
        });
    }
    updateProxy(name, newVal, map) {
        let value;
        if (newVal === null) {
            this.propsProxy[name] = this.ins.rawOpts[name];
        }
        else {
            if (name === 'options') {
                value = strToJson(newVal);
            }
            else {
                value = attrJoinVal(name, newVal, map);
            }
            deepMerge(this.propsProxy, value, {});
        }
    }
    listen({ name, params = [], cb } = {}) {
        name && this.plans.hasOwnProperty(name) ? this.emit(name, ...params) : null;
        if (name && this.propsProxy[`on-${name.toLowerCase()}`]) {
            let fn = new Function('"use strict";return' + this.propsProxy[`on-${name.toLowerCase()}`])();
            fn && fn.call(this, ...params);
        }
        cb && cb.call(this, ...params);
    }
    on(type, handler) {
        plan.add(type, this, handler);
        return this;
    }
    emit(type, ...params) {
        plan.do(type, this, ...params);
        return this;
    }
    off(type, handler) {
        plan.remove(type, this, handler);
        return this;
    }
}

class TreeElem extends CustomElem {
    name;
    value;
    constructor() {
        super();
        this.createShadow();
        super.createPropsObs();
    }
    static get observedAttributes() {
        return [...optTree.map((k) => k.attr), 'options'];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        super.updateProxy(name, newVal, optTree);
        if (name === 'name') {
            this.name = newVal;
        }
    }
    connectedCallback() {
        this.render();
        this.connected = true;
    }
    createShadow() {
        this.shadowEl = this.attachShadow({ mode: "open" });
        this.slotEl = createEl('slot');
        this.shadowEl.appendChild(this.slotEl);
    }
    render() {
        this.wrapEl = createEl('div', { [alias]: 'slot-host' }, this.innerHTML);
        this.innerHTML = '';
        this.appendChild(this.wrapEl);
        this.ins = new Tree(this.wrapEl, {
            onOutput: (data) => {
                this.value = data.value;
            }
        });
        this.ins.inputEl.setAttribute(ax.embedSign, '');
    }
}

class AccordionElem extends CompBaseComm {
    constructor() {
        super();
        this.createShadow();
        super.createPropsObs();
    }
    static get observedAttributes() {
        return [...optAccordion.map((k) => k.attr), 'options'];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (name === 'value') {
            this.ins && newVal && this.ins.setVals(newVal);
        }
        else {
            super.updateProxy(name, newVal, optAccordion);
        }
    }
    render() {
        this.insertSource();
        this.wrapEl = createEl('div', { [alias]: 'slot-host' }, this.rawHtml);
        this.appendChild(this.wrapEl);
        this.ins = new Accordion(this.wrapEl);
        setTimeout(() => {
            this.propsObs.on('completed', (data) => {
                this.ins.update(this.properties);
                this.listen({ name: 'updated', params: [this.properties] });
            });
        }, 500);
    }
}

class EditorElem extends CompBaseCommFieldMixin {
    type;
    select;
    constructor() {
        super();
        this.type = 'editor-comp';
        this.getRawData();
        this.select = () => this.ins.inputEl.select();
    }
    static custAttrs = ['name', 'value', 'delay', 'classes', 'content', 'cont-type', 'cont-data', 'ajax', 'appear', 'header', 'mode', 'min-height', 'max-height', 'feature'];
    static boolAttrs = ['disabled', 'readonly', 'deferred'];
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.propsProxy[name] = this.mapVals(name, newVal, EditorElem, optEditor);
        !EditorElem.evtsArr.includes(name) && this.saveModsOpts(name, optEditor);
        if (name === 'name') {
            let tmp = newVal || '';
            this.ins && (this.ins.inputEl.name = tmp);
            this.name = tmp;
        }
        else if (name === 'value') {
            this.ins && this.ins.setVals(newVal);
        }
        else if (name === 'disabled') {
            this.disabled = this.propsProxy[name];
            if (this.ins) {
                this.disabled ? this.ins.disable() : this.ins.enable();
            }
        }
        else if (name === 'readonly') {
            this.readOnly = this.propsProxy[name];
            if (this.ins) {
                this.readOnly ? this.ins.readonly() : this.ins.enable();
            }
        }
    }
    getRawData() {
        for (let k of [...EditorElem.custAttrs, ...EditorElem.lazyAttrs, ...EditorElem.boolAttrs, ...EditorElem.evtsArr]) {
            this.propsRaw[k] = this.mapVals(k, this.getAttribute(k), EditorElem, optEditor);
        }
        this.getProxyProps();
        this.propsRaw.value = this.rawHtml || this.propsRaw.value || '';
        for (let k of Object.keys(this.properties).filter(keyCond))
            this.saveModsOpts(k, optEditor);
        this.setFieldProps(['name', 'value', 'disabled', 'readOnly']);
    }
    newIns() {
        let params = Object.assign(this.modsOpts['module'], {
            onOutput: (data) => {
                this.modsOpts['module'].value = this.propsProxy.value = this.value = data.value;
                this.canListen = false;
                this.setAttribute('value', data.value);
                this.canListen = true;
                this.dispatchEvent(this.events['input'].event);
                this.listen({ name: 'input', params: [data.value] });
                if (data.value !== data.raw) {
                    this.dispatchEvent(this.events['change'].event);
                    this.listen({ name: 'changed', params: [{ newVal: data.value, oldVal: data.raw }] });
                }
            }
        });
        this.ins = new Editor(this.wrapEl, params);
        this.ins.inputEl.setAttribute(ax.embedSign, '');
        this.focus = () => {
            (elState(this.ins.inputEl).isVisible ? this.ins.inputEl : this.ins.contEl).focus();
        };
        this.blur = () => {
            (elState(this.ins.inputEl).isVisible ? this.ins.inputEl : this.ins.contEl).blur();
        };
        this.clear = () => {
            this.ins.clearVals();
            this.listen({ name: 'cleared' });
        };
        this.ins.inputEl.addEventListener('focus', () => {
            this.dispatchEvent(this.events['focus'].event);
        }, false);
        this.ins.contEl.addEventListener('focus', () => {
            this.dispatchEvent(this.events['focus'].event);
        }, false);
        this.ins.inputEl.addEventListener('blur', () => {
            this.dispatchEvent(this.events['blur'].event);
        }, false);
        this.ins.contEl.addEventListener('blur', () => {
            this.dispatchEvent(this.events['blur'].event);
        }, false);
        this.ins.inputEl.addEventListener('input', (e) => {
            e.stopPropagation();
        }, false);
        this.ins.contEl.addEventListener('input', (e) => {
            e.stopPropagation();
        }, false);
        this.ins.inputEl.addEventListener('change', (e) => {
            e.stopPropagation();
        }, false);
    }
    render() {
        this.insertSource();
        this.wrapEl = createEl('div', {}, this.rawHtml);
        this.appendChild(this.wrapEl);
        this.propsObs.on('completed', (data) => {
            let keyArr = [...EditorElem.custAttrs, ...EditorElem.boolAttrs].filter((k) => !['name', 'disabled', 'value', 'readonly'].includes(k)), intArr = getIntArr([keyArr, data.keys.set]);
            intArr.length && (this.connCount <= 1 ? this.newIns() : this.ins.update(this.modsOpts['module']));
        });
    }
}

class SelectElem extends CompBaseCommFieldMixin {
    type;
    constructor() {
        super();
        this.type = 'select-comp';
        this.getRawData();
    }
    static custAttrs = ['name', 'value', 'field', 'type', 'exclude', 'min', 'max', 'span', 'content', 'cont-type', 'cont-Data', 'ajax', 'size', 'max-height', 'search', 'tools'];
    static boolAttrs = ['manual', 'disabled', 'readonly', 'full', 'multiple', 'sliced', 'removable', 'unique', 'collapse', 'status', 'auto-width'];
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.propsProxy[name] = this.mapVals(name, newVal, SelectElem, optSelect);
        !SelectElem.evtsArr.includes(name) && this.saveModsOpts(name, optSelect);
        if (name === 'name') {
            let tmp = newVal || '';
            this.ins && (this.ins.inputEl.name = tmp);
            this.name = tmp;
        }
        else if (name === 'value') {
            this.ins && this.ins.select(newVal);
        }
        else if (name === 'disabled') {
            this.disabled = this.propsProxy[name];
            if (this.ins) {
                this.disabled ? this.ins.disable() : this.ins.enable();
            }
        }
        else if (name === 'size') {
            if (this.ins) {
                newVal ? this.ins.targetEl.setAttribute('size', newVal) : this.ins.targetEl.removeAttribute('size');
            }
        }
    }
    getRawData() {
        for (let k of [...SelectElem.custAttrs, ...SelectElem.lazyAttrs, ...SelectElem.boolAttrs, ...SelectElem.evtsArr]) {
            this.propsRaw[k] = this.mapVals(k, this.getAttribute(k), SelectElem, optSelect);
        }
        this.getProxyProps();
        for (let k of Object.keys(this.properties).filter(keyCond))
            this.saveModsOpts(k, optSelect);
        this.setFieldProps(['name', 'disabled']);
        this.value = '';
    }
    newIns() {
        let params = Object.assign(this.modsOpts['module'], {
            onOutput: (data) => {
                this.modsOpts['module'].value = this.propsProxy.value = this.value = data.value;
                this.canListen = false;
                this.setAttribute('value', data.value);
                this.canListen = true;
                this.dispatchEvent(this.events['input'].event);
                this.listen({ name: 'input', params: [data.value] });
                if (data.value !== data.raw) {
                    this.dispatchEvent(this.events['change'].event);
                    this.listen({ name: 'changed', params: [{ newVal: data.value, oldVal: data.raw }] });
                }
            }
        });
        this.ins = new Select(this.wrapEl, params);
        this.ins.inputEl.setAttribute(ax.embedSign, '');
        this.focus = () => {
            this.wrapEl.focus();
        };
        this.blur = () => {
            this.wrapEl.blur();
        };
        this.clear = () => {
            this.ins.clear();
            this.listen({ name: 'cleared' });
        };
        this.wrapEl.addEventListener('focus', () => {
            this.dispatchEvent(this.events['focus'].event);
        }, false);
        this.wrapEl.addEventListener('blur', () => {
            this.dispatchEvent(this.events['blur'].event);
        }, false);
    }
    render() {
        this.insertSource();
        this.wrapEl = createEl('div', {}, this.rawHtml);
        this.appendChild(this.wrapEl);
        this.propsObs.on('completed', (data) => {
            let keyArr = [...SelectElem.custAttrs, ...SelectElem.boolAttrs].filter((k) => !['name', 'disabled', 'value', 'size'].includes(k)), intArr = getIntArr([keyArr, data.keys.set]);
            intArr.length && (this.connCount <= 1 ? this.newIns() : this.ins.update(this.modsOpts['module']));
        });
    }
}

class UploadElem extends CompBaseCommFieldMixin {
    type;
    focusEl;
    constructor() {
        super();
        this.type = 'upload-comp';
        this.getRawData();
    }
    static custAttrs = ['name', 'value', 'url', 'content', 'cont-type', 'cont-data', 'ajax', 'limit', 'accept', 'type', 'feature', 'size', 'table', 'classes', 'status', 'choose-btn', 'upload-btn', 'clear-btn', 'cloud'];
    static boolAttrs = ['disabled', 'readonly', 'multiple', 'manual', 'pastable'];
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.propsProxy[name] = this.mapVals(name, newVal, UploadElem, optUpload);
        !UploadElem.evtsArr.includes(name) && this.saveModsOpts(name, optUpload);
        if (name === 'name') {
            let tmp = newVal || '';
            this.ins && (this.ins.inputEl.name = tmp);
            this.name = tmp;
        }
        else if (name === 'value') {
            this.ins && this.ins.add(newVal);
            this.value = this.ins ? this.ins.output.value : newVal;
        }
        else if (name === 'disabled') {
            this.disabled = this.propsProxy[name];
            if (this.ins) {
                this.disabled ? this.ins.lock() : this.ins.unlock();
            }
        }
        else if (name === 'size') {
            if (this.ins) {
                newVal ? this.ins.targetEl.setAttribute('size', newVal) : this.ins.targetEl.removeAttribute('size');
            }
        }
    }
    getRawData() {
        for (let k of [...UploadElem.custAttrs, ...UploadElem.lazyAttrs, ...UploadElem.boolAttrs, ...UploadElem.evtsArr]) {
            this.propsRaw[k] = this.mapVals(k, this.getAttribute(k), UploadElem, optUpload);
        }
        this.getProxyProps();
        for (let k of Object.keys(this.properties).filter(keyCond))
            this.saveModsOpts(k, optUpload);
        this.setFieldProps(['name', 'value', 'disabled']);
    }
    newIns() {
        let params = Object.assign(this.modsOpts['module'], {
            onOutput: (data) => {
                this.modsOpts['module'].value = this.propsProxy.value = this.value = data.value;
                this.canListen = false;
                this.setAttribute('value', data.value);
                this.canListen = true;
                this.dispatchEvent(this.events['input'].event);
                this.listen({ name: 'input', params: [data.value] });
                if (data.value !== data.raw) {
                    this.dispatchEvent(this.events['change'].event);
                    this.listen({ name: 'changed', params: [{ newVal: data.value, oldVal: data.raw }] });
                }
            }
        });
        this.ins = new Upload(this.wrapEl, params);
        this.ins.inputEl.setAttribute(ax.embedSign, '');
        this.focusEl = this.ins.options.feature === 'gallery' ? this.wrapEl : this.ins.chooseBtn;
        this.focus = () => {
            this.focusEl.focus();
        };
        this.blur = () => {
            this.focusEl.blur();
        };
        this.clear = () => {
            this.ins.clear();
            this.listen({ name: 'cleared' });
        };
        this.focusEl.addEventListener('focus', () => {
            this.dispatchEvent(this.events['focus'].event);
        }, false);
        this.focusEl.addEventListener('blur', () => {
            this.dispatchEvent(this.events['blur'].event);
        }, false);
    }
    render() {
        this.insertSource();
        this.wrapEl = createEl('div', {}, this.rawHtml);
        this.appendChild(this.wrapEl);
        this.propsObs.on('completed', (data) => {
            let keyArr = [...UploadElem.custAttrs, ...UploadElem.boolAttrs].filter((k) => !['name', 'disabled', 'value', 'size'].includes(k)), intArr = getIntArr([keyArr, data.keys.set]);
            intArr.length && (this.connCount <= 1 ? this.newIns() : this.ins.update(this.modsOpts['module']));
        });
    }
}

class FieldsElem extends CompBaseCommField {
    labelEl;
    limitEl;
    diskEl;
    cubeEl;
    iconEl;
    btnEl;
    meanEl;
    type;
    childEls;
    compEls;
    fieldsEl;
    unitEl;
    constructor() {
        super();
        this.type = 'fields-comp';
        this.getRawData();
        this.btnEl = getEl(':scope > AX-BTN', this);
        this.childEls = [...this.children].filter((k) => k !== this.btnEl);
        this.compEls = this.childEls.filter((k) => fieldTypes.includes(k.nodeName.toLowerCase().replace('ax-', '')));
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['size', 'label', 'unit', 'connector', 'align', 'shape'];
    static boolAttrs = ['disabled', 'full', 'dividable'];
    static get observedAttributes() {
        return [...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, FieldsElem);
    }
    getRawData() {
        this.getRawProps(FieldsElem);
        this.getProxyProps();
    }
    fillWrap(data) {
        this.fieldsEl = createEl('div', { [alias]: 'fields' });
        this.fieldsEl.append(...this.childEls);
        this.wrapEl = createEl('div', { [alias]: 'wrap' }, this.fieldsEl);
        this.btnEl && this.wrapEl.appendChild(this.btnEl);
        this.labelEl = createEl('span', { [alias]: 'label' });
        this.unitEl = createEl('span', { [alias]: 'unit' });
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
    changedMaps = {
        size: this.changedSize,
        align: this.changedSize,
        label: this.changedLabel,
        unit: this.changedUnit,
        connector: this.changedConn,
        shape: this.changedShape,
    };
    changedLabel(opt) {
        if (opt.newVal) {
            this.labelEl.innerHTML = opt.newVal;
            elState(this.labelEl).isVirtual && this.wrapEl.insertAdjacentElement('afterbegin', this.labelEl);
        }
        else {
            this.labelEl.remove();
        }
    }
    changedUnit(opt) {
        if (opt.newVal) {
            this.unitEl.innerHTML = opt.newVal;
            elState(this.unitEl).isVirtual && this.fieldsEl.insertAdjacentElement('afterend', this.unitEl);
        }
        else {
            this.unitEl.remove();
        }
    }
    changedSize(opt) {
        if (opt.newVal) {
            for (let k of this.compEls)
                k.setAttribute(opt.name, opt.newVal);
            this.btnEl && opt.name === 'size' && this.btnEl.setAttribute(opt.name, opt.newVal);
        }
        else {
            for (let k of this.compEls)
                k.removeAttribute(opt.name);
            this.btnEl && opt.name === 'size' && this.btnEl.removeAttribute(opt.name);
        }
    }
    changedConn(opt) {
        if (opt.newVal) {
            for (let [i, k] of this.childEls.entries())
                (i !== this.childEls.length - 1) && k.insertAdjacentHTML('afterend', `<i ${alias}="connector">${opt.newVal}</i>`);
        }
        else {
            let conns = getEls(`[${alias}="connector"]`, this.fieldsEl);
            for (let k of conns)
                k.remove();
        }
    }
    changedShape(opt) {
        if (opt.newVal) {
            this.btnEl && this.btnEl.setAttribute('shape', opt.newVal);
        }
        else {
            this.btnEl && this.btnEl.removeAttribute('shape');
        }
    }
}

class SearchElem extends CompBaseCommField {
    labelEl;
    limitEl;
    diskEl;
    cubeEl;
    iconEl;
    btnEl;
    meanEl;
    type;
    fieldsEl;
    constructor() {
        super();
        this.type = 'search-comp';
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['size', 'label', 'feature', 'shape'];
    static boolAttrs = ['disabled', 'full', 'notable'];
    static get observedAttributes() {
        return [...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.propsProxy[name] = SearchElem.boolAttrs.includes(name) ? getAttrBool(newVal) : newVal;
        this.changedMaps[name] && this.changedMaps[name].call(this, { name, newVal, oldVal, proxy: this.propsProxy });
    }
    getRawData() {
        for (let k of [...SearchElem.custAttrs, ...SearchElem.lazyAttrs])
            this.propsRaw[k] = this.getAttribute(k);
        for (let k of SearchElem.boolAttrs)
            this.propsRaw[k] = getAttrBool(this.getAttribute(k));
        this.getProxyProps();
    }
    fillWrap(data) {
    }
    render() {
    }
    changedMaps = {
        full: this.changedFull,
        size: this.changedSize,
        notable: this.changedNotable,
        shape: this.changedShape,
    };
    changedFull(opt) {
        this.fieldsEl = this.querySelector('ax-fields');
        if (!this.fieldsEl)
            return;
        this.fieldsEl.toggleAttribute('full', !!opt.newVal);
    }
    changedSize(opt) {
        this.fieldsEl = this.querySelector('ax-fields');
        if (!this.fieldsEl)
            return;
        if (opt.newVal) {
            this.fieldsEl.setAttribute('size', opt.newVal);
        }
        else {
            this.fieldsEl.removeAttribute('size');
        }
    }
    changedNotable(opt) {
        this.fieldsEl = this.querySelector('ax-fields');
        if (!this.fieldsEl.btnEl)
            return;
        if (this.propsProxy.notable) {
            this.fieldsEl.btnEl.setAttribute('theme', 'prim');
        }
        else {
            this.fieldsEl.btnEl.removeAttribute('theme');
        }
    }
    changedShape(opt) {
        this.fieldsEl = this.querySelector('ax-fields');
        if (!this.fieldsEl.btnEl)
            return;
        if (opt.newVal) {
            this.fieldsEl.setAttribute('shape', opt.newVal);
        }
        else {
            this.fieldsEl.removeAttribute('shape');
        }
    }
}

class CalloutElem extends CompBaseComm {
    resultEl;
    iconEl;
    diskEl;
    cubeEl;
    imageEl;
    closeEl;
    linkEl;
    maskEl;
    toolsEl;
    captEl;
    contEl;
    bodyEl;
    progEl;
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['theme', 'caption', 'icon', 'disk', 'cube', 'image', 'href', 'target', 'rel', 'feature', 'autoclose', 'size', ...this.baseAttrs];
    static boolAttrs = ['closable', 'square', 'opaque', 'notable', 'hidden', 'result'];
    static get observedAttributes() {
        return ['content', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, CalloutElem);
    }
    getRawData() {
        this.getRawProps(CalloutElem);
        this.propsRaw.content = this.getAttribute('content') || this.rawHtml;
        this.getProxyProps();
    }
    fillWrap(data) {
        this.wrapEl = createEl('div', { [alias]: 'wrap' });
        this.resultEl = createEl('ax-result');
        this.iconEl = createEl('i', { [alias]: 'icon' });
        this.diskEl = createEl('i', { [alias]: 'disk' });
        this.cubeEl = createEl('i', { [alias]: 'cube' });
        this.imageEl = createEl('i', { [alias]: 'image' });
        this.closeEl = createEl('span', { [alias]: 'close' }, `<i class="${prefix}icon-close"></i>`);
        this.linkEl = createEl('a', { [alias]: 'link' }, `<i class="${prefix}icon-right-up"></i>`);
        this.maskEl = createEl('a', { [alias]: 'mask' });
        this.toolsEl = createEl('span', { [alias]: 'tools', class: `${prefix}box-tools` });
        this.captEl = createEl('div', { [alias]: 'caption' });
        this.contEl = createEl('div', { [alias]: 'content' }, this.propsProxy.content);
        this.bodyEl = createEl('div', { [alias]: 'body' }, this.contEl);
        this.progEl = createEl('ax-progress', { [alias]: 'prog', linecap: 'square', thk: 'xs', theme: 'warn', value: '100', label: false });
        this.wrapEl.append(this.bodyEl, this.toolsEl);
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
        this.propsProxy.autoclose && this.progEl.setAttribute('value', 0);
        this.closeEl.onclick = () => {
            this.toggleAttribute('hidden', true);
        };
    }
    changedMaps = {
        theme: this.changedTheme,
        caption: this.changedCapt,
        content: this.changedCont,
        icon: this.changedIcon,
        disk: this.changedDisk,
        cube: this.changedDisk,
        image: this.changedDisk,
        result: this.changedResult,
        href: this.changedHref,
        ref: this.changedRel,
        target: this.changedRel,
        closable: this.changedClose,
        feature: this.changedFeature,
        hidden: this.changedHidden,
        autoclose: this.changedAuto,
    };
    changedTheme(opt) {
        this.progEl.setAttribute('theme', opt.newVal || 'warn');
    }
    changedHidden(opt) {
        if (this.propsProxy.hidden) {
            slideUp({
                el: this,
                done: () => {
                    this.listen({ name: 'hidden' });
                }
            });
        }
        else {
            slideDown({
                el: this,
                done: () => {
                    this.listen({ name: 'shown' });
                }
            });
        }
    }
    changedCapt(opt) {
        if (opt.newVal) {
            this.captEl.innerHTML = opt.newVal;
            elState(this.captEl).isVirtual && this.contEl.insertAdjacentElement('beforebegin', this.captEl);
        }
        else {
            this.captEl.remove();
        }
    }
    changedCont(opt) {
        this.contEl.innerHTML = opt.newVal || '';
    }
    changedIcon(opt) {
        if (opt.newVal) {
            this.iconEl.setAttribute('class', opt.newVal);
            elState(this.iconEl).isVirtual && this.wrapEl.insertAdjacentElement('afterbegin', this.iconEl);
        }
        else {
            this.iconEl.remove();
        }
    }
    changedDisk(opt) {
        let target = this[opt.name + 'El'];
        if (opt.newVal) {
            target.innerHTML = `<img src="${opt.newVal}"/>`;
            elState(target).isVirtual && this.wrapEl.insertAdjacentElement('afterbegin', target);
        }
        else {
            target.remove();
        }
    }
    changedResult(opt) {
        if (this.propsProxy.result) {
            elState(this.resultEl).isVirtual && this.wrapEl.insertAdjacentElement('afterbegin', this.resultEl);
            this.resultEl.setAttribute('to', this.propsProxy.theme || 'warn');
        }
        else {
            this.resultEl.remove();
        }
    }
    changedHref(opt) {
        if (opt.newVal) {
            this.maskEl.setAttribute('href', opt.newVal);
            this.linkEl.setAttribute('href', opt.newVal);
            elState(this.maskEl).isVirtual && this.wrapEl.appendChild(this.maskEl);
            elState(this.linkEl).isVirtual && this.toolsEl.insertAdjacentElement('afterbegin', this.linkEl);
        }
        else {
            this.maskEl.remove();
            this.linkEl.remove();
        }
    }
    changedRel(opt) {
        if (opt.newVal) {
            this.maskEl.setAttribute(opt.name, opt.newVal);
            this.linkEl.setAttribute(opt.name, opt.newVal);
        }
        else {
            this.maskEl.removeAttribute(opt.name);
            this.linkEl.removeAttribute(opt.name);
        }
    }
    changedClose(opt) {
        if (this.propsProxy.closable) {
            this.toolsEl.appendChild(this.closeEl);
        }
        else {
            this.closeEl.remove();
        }
    }
    changedFeature(opt) {
        if (opt.newVal === 'alert') {
            this.setAttribute('icon', `${prefix}icon-warn-o-f`);
            this.setAttribute('theme', 'warn');
            this.toggleAttribute('notable', true);
        }
    }
    changedAuto(opt) {
        if (opt.newVal !== null) {
            this.progEl.setAttribute('duration', opt.newVal || 3000);
            elState(this.progEl).isVirtual && this.wrapEl.appendChild(this.progEl);
            this.progEl?.ins?.on('zero', () => {
                this.closeEl.click();
            });
            this.onmouseenter = () => {
                this.progEl.ins.pause();
            };
            this.onmouseleave = () => {
                this.progEl.ins.continue();
            };
            this.progEl.setAttribute('value', 0);
        }
        else {
            this.progEl.remove();
        }
    }
}

class TwilightElem extends CompBaseComm {
    labelEl;
    legendEl;
    targetEl;
    labels;
    isDay;
    constructor() {
        super();
        this.getRawData();
        this.labels = [config.lang.twilight.day, config.lang.twilight.night];
        this.isDay = true;
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['labels', 'target', 'feature', ...this.baseAttrs];
    static boolAttrs = [''];
    static get observedAttributes() {
        return [...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, TwilightElem);
    }
    getRawData() {
        this.getRawProps(TwilightElem);
        this.getProxyProps();
    }
    fillWrap(data) {
        this.wrapEl = createEl('div', { [alias]: 'wrap' });
        this.labelEl = createEl('i', { [alias]: 'label' }, this.labels[0]);
        let now = Date.now();
        this.legendEl = createEl('i', { [alias]: 'legend' }, `
                <svg viewBox="0 0 24 24">
                    <mask id="mask-${now}">
                        <rect x="0" y="0" width="100%" height="100%" fill="white" />
                        <circle cx="25" cy="13" r="6" stroke="none" class="${prefix}twilight-mask" fill="dark" />
                    </mask>
                    <circle fill="currentColor" cx="12" cy="12" r="6" class="${prefix}twilight-main" mask="url(#mask-${now})" />
                    <g stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="${prefix}twilight-rays">
                        <line x1="12" y1="1" x2="12" y2="3" />
                        <line x1="12" y1="21" x2="12" y2="23" />
                        <line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
                        <line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
                        <line x1="1" y1="12" x2="3" y2="12" />
                        <line x1="21" y1="12" x2="23" y2="12" />
                        <line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
                        <line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
                    </g>
                </svg>
        `);
        this.wrapEl.append(this.labelEl, this.legendEl);
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
        this.addEventListener('click', () => {
            let target = this.targetEl || document.body;
            if (target.hasAttribute('scheme')) {
                this.labelEl.innerHTML = this.labels[0];
                target.removeAttribute('scheme');
                this.isDay = true;
            }
            else {
                this.labelEl.innerHTML = this.labels[1];
                target.setAttribute('scheme', 'dark');
                this.isDay = false;
            }
        }, false);
    }
    changedMaps = {
        feature: this.changedFeature,
        target: this.changedTarget,
        labels: this.changedLabels,
    };
    changedTarget(opt) {
        this.targetEl = getEl(opt.newVal);
    }
    changedFeature(opt) {
        opt.newVal ? this.labelEl.remove() : this.wrapEl.insertAdjacentElement('afterbegin', this.labelEl);
    }
    changedLabels(opt) {
        if (opt.newVal) {
            this.labels = valToArr(opt.newVal);
            this.labelEl.innerHTML = this.labels[this.isDay ? 0 : 1];
        }
    }
}

class PaginationElem extends CompBaseComm {
    constructor() {
        super();
        this.createShadow();
        super.createPropsObs();
    }
    static get observedAttributes() {
        return [...optPagination.map((k) => k.attr), 'options'];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (name === 'current') {
            this.ins && newVal && this.ins.locate(newVal);
        }
        else {
            super.updateProxy(name, newVal, optPagination);
        }
    }
    render() {
        this.insertSource();
        this.wrapEl = createEl('div', { [alias]: 'slot-host' }, this.rawHtml);
        this.appendChild(this.wrapEl);
        this.ins = new Pagination(this.wrapEl);
        setTimeout(() => {
            this.propsObs.on('completed', (data) => {
                this.ins.update(this.properties);
                this.listen({ name: 'updated', params: [this.properties] });
            });
        }, 500);
    }
}

class StepElem extends CompBaseComm {
    content;
    template;
    constructor() {
        super();
        this.template = `
        {{let active = this.active?'active':'',
            href = this.href ?'href="'+this.href+'"':'',
            name = href?'a':'div',
            target = href && this.target?'target="'+this.target+'"':'',
            rel= href && this.rel?'rel="'+this.rel+'"':'';
        /}}
        <{{name}} {{href}} {{target}} {{rel}} {{active}} class="_step-item _reset">
            <div class="_step-track">
                <div></div>
                <span ${alias}="node"></span>
                <div></div>
            </div>
        </{{name}}>
    `;
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['active', 'type', 'theme', 'dir', 'error', ...this.baseAttrs];
    static boolAttrs = ['head-show', 'body-show', 'inverted', 'justify'];
    static get observedAttributes() {
        return ['content', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, StepElem);
    }
    getRawData() {
        this.getRawProps(StepElem);
        this.propsRaw.content = this.getAttribute('content') || this.rawHtml;
        this.getProxyProps();
    }
    fillWrap(data) {
        this.content = this.stdContent(this.getArrContent());
        let fragment = document.createDocumentFragment();
        (this.content);
        for (let k of this.content) {
            let node = tplToEl(renderTpl(this.template, k));
            k.wrapEl = node;
            k.wrapEl.querySelector(`[${alias}="node"]`).innerHTML = this.getLegend(k);
            k.headEl = createEl('div', { class: `${prefix}step-head` }, k.tips);
            k.labelEl = createEl('div', { [alias]: 'label' }, k.label);
            k.briefEl = createEl('div', { [alias]: 'brief' }, k.brief);
            k.bodyEl = createEl('div', { class: `${prefix}step-body` }, [k.labelEl, k.briefEl]);
            fragment.appendChild(node);
        }
        this.wrapEl = createEl('div', { [alias]: 'wrap' }, data.label);
        this.wrapEl.appendChild(fragment);
    }
    getArrContent(content = this.propsProxy.content) {
        let trim = content.trim(), tmp = createEl('div', {}, trim), firstChild = tmp.firstElementChild, result = [];
        if (trim.startsWith('<') && firstChild) {
            let nodeName = firstChild.nodeName;
            if (nodeName === 'SCRIPT' && firstChild.getAttribute('type').includes('content')) {
                result = parseStr({
                    content: firstChild.textContent,
                    type: 'array',
                    error: (err) => {
                        console.info(config.error.parse, err);
                    }
                });
            }
            else {
                result = [...tmp.children].map((k, i) => {
                    let _tmp = {
                        label: k.getAttribute('label') || k.textContent.trim(),
                        brief: k.getAttribute('brief'),
                        tips: k.getAttribute('tips'),
                        icon: k.getAttribute('icon'),
                        cube: k.getAttribute('cube'),
                        image: k.getAttribute('image'),
                        disk: k.getAttribute('disk'),
                    };
                    if (k.nodeName === 'A') {
                        _tmp.target = k.getAttribute('target');
                        _tmp.rel = k.getAttribute('rel');
                        _tmp.href = k.getAttribute('href');
                    }
                    return _tmp;
                });
            }
        }
        else {
            result = parseStr({
                content,
                error: (err) => {
                    console.info(config.error.parse, err);
                }
            });
        }
        return result;
    }
    stdContent(content) {
        return content.map((k, i) => {
            if (typeof k === 'string' || typeof k === 'number') {
                return { label: k, index: i + 1 };
            }
            else {
                return Object.assign(k, { index: i + 1 });
            }
        });
    }
    getLegend(data) {
        let type = this.propsProxy.type, dft = data.icon ? '<i class="' + data.icon + '"></i>' : data.image ? '<img src="' + data.image + '"/>' : data.index, result = '';
        if (type?.includes('circle')) {
            result = dft;
        }
        else if (type === 'plain') {
            result = data.field ? (data.field === 'icon' ? '<i class="' + data.icon + '"></i>' : data.field === 'image' ? '<img src="' + data.image + '"/>' : data[data.field]) : dft;
        }
        return result;
    }
    setLegendType() {
        for (let k of this.content) {
            let tmp = this.getLegend(k);
            k.wrapEl.querySelector(`[${alias}="node"]`).innerHTML = tmp;
        }
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
    changedMaps = {
        'head-show': this.changedHeadshow,
        'body-show': this.changedBodyshow,
        active: this.changedActive,
        error: this.changedError,
        content: this.changedContent,
        type: this.changedType,
    };
    changedHeadshow(opt) {
        for (let k of this.content) {
            (this.propsProxy['head-show']);
            this.propsProxy['head-show'] ? k.wrapEl.insertAdjacentElement('afterbegin', k.headEl) : k.headEl.remove();
        }
    }
    changedBodyshow(opt) {
        for (let k of this.content) {
            this.propsProxy['body-show'] ? k.wrapEl.insertAdjacentElement('beforeend', k.bodyEl) : k.bodyEl.remove();
        }
    }
    changedActive(opt) {
        if (opt.newVal) {
            let tmp = Math.min(~~opt.newVal, this.content.length - 1);
            for (let [i, k] of this.content.entries()) {
                k.wrapEl.toggleAttribute('active', i === tmp ? true : false);
            }
        }
    }
    changedError(opt) {
        let tmp = opt.newVal ? valToArr(opt.newVal) : [];
        if (!tmp.length) {
            for (let k of this.content) {
                k.wrapEl.toggleAttribute('error', false);
            }
        }
        else {
            for (let [i, k] of this.content.entries()) {
                k.wrapEl.toggleAttribute('error', tmp.includes(i) ? true : false);
            }
        }
    }
    changedContent(opt) {
        if (opt.newVal) {
            this.wrapEl.remove();
            this.fillWrap(this.propsProxy);
            this.appendChild(this.wrapEl);
        }
    }
    changedType(opt) {
        this.setLegendType();
    }
}

class StatusElem extends CompBaseComm {
    lampEl;
    labelEl;
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['type', 'lamp', 'current', ...this.baseAttrs];
    static boolAttrs = [];
    static get observedAttributes() {
        return ['label', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, StatusElem);
    }
    getRawData() {
        this.getRawProps(StatusElem);
        this.propsRaw.label = this.getAttribute('label') || this.rawHtml;
        this.getProxyProps();
    }
    fillWrap(data) {
        this.lampEl = createEl('i', { [alias]: 'lamp' });
        this.labelEl = createEl('i', { [alias]: 'label' }, this.propsProxy.lang?.info || config.lang.status.info);
        this.wrapEl = createEl('div', { [alias]: 'wrap' }, this.lampEl);
        this.propsRaw.label && this.wrapEl.appendChild(this.labelEl);
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
    changedMaps = {
        label: this.changedLabel,
        lamp: this.changedLamp,
        current: this.changedCurrent,
    };
    changedLabel(opt) {
        if (opt.newVal === null) {
            this.labelEl.remove();
        }
        else {
            elState(this.labelEl).isVirtual && this.wrapEl.appendChild(this.labelEl);
            let tmp = this.propsProxy.current || 'info';
            this.labelEl.innerHTML = opt.newVal ? opt.newVal : this.propsProxy.lang?.[tmp] || config.lang.status[tmp];
        }
    }
    changedLamp(opt) {
        let attr = this.propsProxy.type === 'circle' ? 'borderColor' : 'background';
        this.lampEl.style[attr] = opt.newVal || null;
    }
    changedCurrent(opt) {
        if (opt.newVal) {
            this.labelEl.innerHTML = this.propsProxy.lang?.[opt.newVal] || config.lang.status[opt.newVal];
        }
    }
}

class HeadingElem extends CompBaseComm {
    headEl;
    iconEl;
    diskEl;
    cubeEl;
    imageEl;
    labelEl;
    tipsEl;
    arrowEl;
    legends;
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = [...this.baseAttrs, 'size', 'theme', 'icon', 'disk', 'cube', 'image', 'tips', 'arrow'];
    static get observedAttributes() {
        return ['label', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, HeadingElem);
    }
    getRawData() {
        this.getRawProps(HeadingElem);
        this.propsRaw.label = this.getAttribute('label') || this.rawHtml;
        this.getProxyProps();
    }
    fillWrap(data) {
        this.labelEl = createEl('i', { [alias]: 'label' }, data.label);
        this.headEl = createEl('div', { [alias]: 'head' }, this.labelEl);
        this.wrapEl = createEl('div', { [alias]: 'wrap' }, this.headEl);
        this.arrowEl = createEl('i', { [alias]: 'arrow', class: `${prefix}icon-right` });
        this.tipsEl = createEl('i', { [alias]: 'tips' }, data.tips);
        this.iconEl = createEl('i', { [alias]: 'icon', class: data.label });
        this.diskEl = createEl('img', { [alias]: 'disk', src: data.disk });
        this.cubeEl = createEl('img', { [alias]: 'cube', src: data.cube });
        this.imageEl = createEl('img', { [alias]: 'image', src: data.image });
        this.legends = [
            { name: 'icon', el: this.iconEl, attr: 'class' },
            { name: 'disk', el: this.diskEl, attr: 'src' },
            { name: 'cube', el: this.cubeEl, attr: 'src' },
            { name: 'image', el: this.imageEl, attr: 'src' },
        ];
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
    changedMaps = {
        label: this.changedLabel,
        icon: this.changedLegend,
        disk: this.changedLegend,
        cube: this.changedLegend,
        image: this.changedLegend,
        tips: this.changedTips,
        arrow: this.changedArrow,
    };
    changedLabel(opt) {
        this.labelEl.innerHTML = opt.newVal;
    }
    changedTips(opt) {
        if (opt.newVal === null) {
            this.tipsEl.remove();
        }
        else {
            this.tipsEl.innerHTML = opt.newVal;
            this.headEl.insertAdjacentElement('afterend', this.tipsEl);
        }
    }
    changedArrow(opt) {
        if (opt.newVal === null) {
            this.arrowEl.remove();
        }
        else {
            this.arrowEl.setAttribute('class', opt.newVal ? opt.newVal : `${prefix}icon-right`);
            this.wrapEl.appendChild(this.arrowEl);
        }
    }
    changedLegend(opt) {
        let item = this.legends.find((k) => k.name === opt.name);
        if (opt.newVal) {
            item.el.setAttribute(item.attr, opt.newVal);
            elState(item.el).isVirtual && this.headEl.insertAdjacentElement('beforebegin', item.el);
            for (let k of this.legends) {
                k !== item && !elState(k.el).isVirtual && k.el.remove();
            }
        }
        else {
            item.el.remove();
        }
    }
}

class SkeletonElem extends CompBaseComm {
    headEl;
    iconEl;
    diskEl;
    cubeEl;
    imageEl;
    labelEl;
    tipsEl;
    arrowEl;
    legends;
    cols;
    rows;
    constructor() {
        super();
        this.getRawData();
        this.fillWrap(this.propsProxy);
    }
    static custAttrs = ['type', 'anim', 'cols', 'rows', ...this.baseAttrs];
    static get observedAttributes() {
        return ['content', ...this.custAttrs, ...this.boolAttrs, ...this.jsonAttrs];
    }
    attributeChangedCallback(name, oldVal, newVal) {
        if (!this.canListen)
            return;
        this.savePropsToListen(name, oldVal, newVal, SkeletonElem);
    }
    getRawData() {
        this.getRawProps(SkeletonElem);
        this.propsRaw.content = this.getAttribute('content') || this.rawHtml;
        this.getProxyProps();
        this.cols = this.getCols();
        this.rows = this.getRows();
    }
    fillWrap(data) {
        this.wrapEl = createEl('div', { [alias]: 'wrap' }, this.getSkelStr());
    }
    getRows(str = this.propsProxy.rows) {
        return ~~this.propsProxy.rows || 1;
    }
    getCols(str = this.propsProxy.cols) {
        let val = null, obj;
        if (isNull(str))
            return val;
        if (str?.includes(',')) {
            obj = strToJson(str);
            val = ~~obj[getScreenSize()];
            (getScreenSize());
        }
        else {
            val = ~~str;
        }
        return val || 4;
    }
    getSkelStr() {
        if (!this.propsProxy.type) {
            if (this.propsProxy.content) {
                (this.propsProxy.content, this.cols, this.rows, (this.cols || 1) * this.rows);
                return this.propsProxy.content.repeat((this.cols || 1) * this.rows);
            }
            else {
                return '';
            }
        }
        if (this.propsProxy.type === 'article') {
            return `
            <section>
                <div rep="legend">
                </div>
                <div rep="text">
                ${'<div></div>'.repeat(this.rows > 1 ? this.rows : 19)}
                </div>
            </section>
            `;
        }
        else {
            let repeatNum = this.propsProxy.type.includes('avatar') ? 2 :
                this.propsProxy.type === 'paragraph' ? 4 : 3, itemStr = `
        <section>
                <div rep="legend"></div>
                <div rep="text">
                ${'<div></div>'.repeat(repeatNum)}
                </div>
            </section>
        `;
            return itemStr.repeat((this.cols || 1) * this.rows);
        }
    }
    render() {
        this.insertSource();
        this.appendChild(this.wrapEl);
    }
    changedMaps = {
        content: this.changedContent,
        cols: this.changedCols,
        rows: this.changedRows,
        type: this.changedType
    };
    changedContent(opt) {
        this.wrapEl.innerHTML = this.getSkelStr();
    }
    changedType(opt) {
        this.wrapEl.innerHTML = this.getSkelStr();
    }
    changedCols(opt) {
        this.cols = this.getCols(opt.newVal);
        this.wrapEl.style.gridTemplateColumns = this.cols ? `repeat(${this.cols}, 1fr)` : null;
        this.wrapEl.innerHTML = this.getSkelStr();
    }
    changedRows(opt) {
        this.rows = this.getRows(opt.newVal);
        this.wrapEl.innerHTML = this.getSkelStr();
    }
}

const init = (type, parent) => {
    let parentEl = getEl(parent) || document.body, evalFn = new Function('el', 'module', `"use strict";try {return new module(el)} catch {return null}`), moduleNodeList = [], setProp = (node, module) => {
        elProps(node)?.add(module);
    }, getUsableModules = () => {
        let result = [];
        for (let [key, value] of Object.entries(ax)) {
            value.hostType === 'node' ? result.push(key) : null;
        }
        return result;
    }, getNodeList = (types) => {
        let allModules = getUsableModules(), modules = !isEmpty(types) ? types.filter(k => allModules.includes(k)) : allModules, modulesNodes = modules.map(k => {
            return { module: k, nodeList: [...parentEl.querySelectorAll(`[ax-${k}]`)].filter((i) => !i.ax || (i.ax && !i.ax[k])) };
        });
        return modulesNodes;
    }, activeFun = (obj) => {
        if (['Dialog', 'Drawer', 'Popup'].includes(obj.module)) {
            higherParent(obj.module, obj.nodeList);
        }
        else {
            obj.nodeList.forEach((k) => {
                let ins = evalFn(k, ax[obj.module]);
                ins ? setProp(k, obj.module) : null;
            });
        }
    }, higherParent = (module, nodes) => {
        let eachInstance = (arr, parent) => {
            arr.forEach((k) => {
                let ins = evalFn(k, ax[module]), children;
                if (ins) {
                    k[module] = true;
                    parent ? ins.targetEl.style.zIndex = parseInt(getComputedStyle(parent.targetEl).zIndex) + 1 : null;
                    children = [...ins.contEl.querySelectorAll(`[ax-${module}]`)];
                    children.length > 0 ? eachInstance(children, ins) : null;
                }
            });
        };
        eachInstance(nodes);
    };
    if (!isEmpty(type)) {
        let moduleType = getDataType(type), types;
        types = (moduleType === 'String') ? [type] :
            (moduleType === 'Array') ? type : [];
        moduleNodeList = types.length > 0 ? getNodeList(types) : [];
    }
    else {
        moduleNodeList = getNodeList();
    }
    moduleNodeList.length > 0 && decompTask({ tasks: moduleNodeList, run: (task) => { activeFun(task); } });
    let lazySrcs = getEls('[lazy-src]', parentEl), lazyAsyncs = getEls('[lazy-async]', parentEl);
    for (let k of lazySrcs) {
        if (k?.ax?.lazy)
            continue;
        new Lazy(k, {
            content: k.getAttribute('lazy-src'),
            type: 'src'
        });
    }
    for (let k of lazyAsyncs) {
        if (k?.ax?.lazy)
            continue;
        new Lazy(k, {
            content: k.getAttribute('lazy-async'),
            contType: k.getAttribute('lazy-type'),
            contData: strToJson(k.getAttribute('lazy-data')),
            tplStr: k.getAttribute('lazy-tpl'),
            type: 'async'
        });
    }
    return ax;
};

var ax_comm = {
    ax,
    alias,
    config,
    fieldTypes,
    prefix,
    getDataType,
    renderTpl,
    getScreenSize,
    sliceStrEnd,
    delay,
    toNumber,
    toPixel,
    preventDft,
    isMobi,
    events,
    icons,
    getFullGap,
    propsMap,
    augment,
    privacy,
    support,
    requireTypes,
    trim,
    getSelectorType,
    isEmpty,
    isNull,
    getEl,
    deepClone,
    deepMerge,
    strToJson,
    attrToJson,
    attrJoinVal,
    extend,
    plan,
    instance,
    breakpoints,
    createEl,
    purifyHtml,
    startUpper,
    storage,
    isDateStr,
    getExpiration,
    ajax,
    offset,
    elState,
    contains,
    getEls,
    getContent,
    setContent,
    createBtns,
    createFooter,
    createTools,
    tplToEl,
    tplToEls,
    getHeights,
    getWidths,
    getElSpace,
    slideDown,
    slideUp,
    slideToggle,
    curveFns,
    ease,
    easeHeight,
    fadeIn,
    fadeOut,
    fadeToggle,
    style,
    show,
    hide,
    toggle,
    pipe,
    spreadBool,
    getClasses,
    classes,
    validTools,
    toLocalTime,
    regExps,
    replaceFrag,
    combineArr,
    sliceFrags,
    paramToJson,
    getLast,
    fieldTools,
    isSubset,
    debounce,
    throttle,
    elProps,
    convertByte,
    clampVal,
    formTools,
    fileTools,
    unique,
    increaseId,
    treeTools,
    valToArr,
    moveItem,
    getValsFromAttrs,
    dl2Tree,
    findItem,
    findItems,
    getAttrBool,
    getAttrArr,
    theme,
    attrValBool,
    getBetweenEls,
    scrollTo,
    allToEls,
    getUTCTimestamp,
    setAttr,
    setAttrs,
    getScrollObj,
    transformTools,
    isScrollUp,
    eventMap,
    getClientObj,
    bulletTools,
    arrSearch,
    arrSort,
    isProxy,
    getNestProp,
    getIntArr,
    clearRegx,
    isOutside,
    getRectPoints,
    elsSort,
    getEvtTarget,
    getStrFromTpl,
    stdParam,
    setSingleSel,
    splitNum,
    parseUrlArr,
    getPlaces,
    dateTools,
    repeatStr,
    createModule,
    createComp,
    getArrMap,
    includes,
    capStart,
    removeItem,
    deepEqual,
    splice,
    appendEls,
    decompTask,
    filterPrims,
    createEvt,
    confirm,
    alert,
    notice,
    prompt,
    keyCond,
    removeStyle,
    removeStyles,
    addStyle,
    addStyles,
    regElem,
    getComputedVar,
    getImgSpin,
    getImgSpinDk,
    getImgNone,
    getImgEmpty,
    getImgAvatar,
    select2Tree,
    ul2Tree,
    parseStr,
    getAutoDur,
    getHypotenuse,
    ModBase,
    ModBaseListen,
    ModBaseListenCache,
    ModBaseListenCacheBubble,
    ModBaseListenCacheNest,
    More,
    Observe,
    Position,
    Hover,
    Popup,
    Dodge,
    Message,
    Valid,
    Menu,
    Tab,
    Drawer,
    Dialog,
    Spy,
    Tooltip,
    Dropdown,
    Gesture,
    Tags,
    Retrieval,
    Autocomplete,
    Scroll,
    Drag,
    Masonry,
    Swipe,
    Lazy,
    Progress,
    Infinite,
    Virtualize,
    Pagination,
    Range,
    Datetime,
    Tree,
    Rate,
    Accordion,
    Editor,
    Select,
    Upload,
    Panel,
    Router,
    Flip,
    CompBase,
    CompBaseComm,
    CompBaseCommField,
    CompBaseCommFieldMixin,
    MoreElem,
    ResultElem,
    DeformElem,
    RadioElem,
    CheckboxElem,
    BtnElem,
    LineElem,
    AvatarElem,
    FormatElem,
    FlagElem,
    BuoyElem,
    GoodElem,
    AnchorsElem,
    MenuElem,
    FileElem,
    InputElem,
    TextareaElem,
    RadiosElem,
    CheckboxesElem,
    NumberElem,
    RangeElem,
    StatsElem,
    IconElem,
    BadgeElem,
    DividerElem,
    AlarmElem,
    ProgressElem,
    DatetimeElem,
    RateElem,
    TreeElem,
    AccordionElem,
    EditorElem,
    SelectElem,
    UploadElem,
    FieldsElem,
    SearchElem,
    CalloutElem,
    TwilightElem,
    PaginationElem,
    StepElem,
    StatusElem,
    HeadingElem,
    SkeletonElem,
    init,
};

exports.Accordion = Accordion;
exports.AccordionElem = AccordionElem;
exports.AlarmElem = AlarmElem;
exports.AnchorsElem = AnchorsElem;
exports.Autocomplete = Autocomplete;
exports.AvatarElem = AvatarElem;
exports.BadgeElem = BadgeElem;
exports.BtnElem = BtnElem;
exports.BuoyElem = BuoyElem;
exports.CalloutElem = CalloutElem;
exports.CheckboxElem = CheckboxElem;
exports.CheckboxesElem = CheckboxesElem;
exports.CompBase = CompBase;
exports.CompBaseComm = CompBaseComm;
exports.CompBaseCommField = CompBaseCommField;
exports.CompBaseCommFieldMixin = CompBaseCommFieldMixin;
exports.Datetime = Datetime;
exports.DatetimeElem = DatetimeElem;
exports.DeformElem = DeformElem;
exports.Dialog = Dialog;
exports.DividerElem = DividerElem;
exports.Dodge = Dodge;
exports.Drag = Drag;
exports.Drawer = Drawer;
exports.Dropdown = Dropdown;
exports.Editor = Editor;
exports.EditorElem = EditorElem;
exports.FieldsElem = FieldsElem;
exports.FileElem = FileElem;
exports.FlagElem = FlagElem;
exports.Flip = Flip;
exports.FormatElem = FormatElem;
exports.Gesture = Gesture;
exports.GoodElem = GoodElem;
exports.HeadingElem = HeadingElem;
exports.Hover = Hover;
exports.IconElem = IconElem;
exports.Infinite = Infinite;
exports.InputElem = InputElem;
exports.Lazy = Lazy;
exports.LineElem = LineElem;
exports.Masonry = Masonry;
exports.Menu = Menu;
exports.MenuElem = MenuElem;
exports.Message = Message;
exports.ModBase = ModBase;
exports.ModBaseListen = ModBaseListen;
exports.ModBaseListenCache = ModBaseListenCache;
exports.ModBaseListenCacheBubble = ModBaseListenCacheBubble;
exports.ModBaseListenCacheNest = ModBaseListenCacheNest;
exports.More = More;
exports.MoreElem = MoreElem;
exports.NumberElem = NumberElem;
exports.Observe = Observe;
exports.Pagination = Pagination;
exports.PaginationElem = PaginationElem;
exports.Panel = Panel;
exports.Popup = Popup;
exports.Position = Position;
exports.Progress = Progress;
exports.ProgressElem = ProgressElem;
exports.RadioElem = RadioElem;
exports.RadiosElem = RadiosElem;
exports.Range = Range;
exports.RangeElem = RangeElem;
exports.Rate = Rate;
exports.RateElem = RateElem;
exports.ResultElem = ResultElem;
exports.Retrieval = Retrieval;
exports.Router = Router;
exports.Scroll = Scroll;
exports.SearchElem = SearchElem;
exports.Select = Select;
exports.SelectElem = SelectElem;
exports.SkeletonElem = SkeletonElem;
exports.Spy = Spy;
exports.StatsElem = StatsElem;
exports.StatusElem = StatusElem;
exports.StepElem = StepElem;
exports.Swipe = Swipe;
exports.Tab = Tab;
exports.Tags = Tags;
exports.TextareaElem = TextareaElem;
exports.Tooltip = Tooltip;
exports.Tree = Tree;
exports.TreeElem = TreeElem;
exports.TwilightElem = TwilightElem;
exports.Upload = Upload;
exports.UploadElem = UploadElem;
exports.Valid = Valid;
exports.Virtualize = Virtualize;
exports.addStyle = addStyle;
exports.addStyles = addStyles;
exports.ajax = ajax;
exports.alert = alert;
exports.alias = alias;
exports.allToEls = allToEls;
exports.appendEls = appendEls;
exports.arrSearch = arrSearch;
exports.arrSort = arrSort;
exports.attrJoinVal = attrJoinVal;
exports.attrToJson = attrToJson;
exports.attrValBool = attrValBool;
exports.augment = augment;
exports.ax = ax;
exports.breakpoints = breakpoints;
exports.bulletTools = bulletTools;
exports.capStart = capStart;
exports.clampVal = clampVal;
exports.classes = classes;
exports.clearRegx = clearRegx;
exports.combineArr = combineArr;
exports.config = config;
exports.confirm = confirm;
exports.contains = contains;
exports.convertByte = convertByte;
exports.createBtns = createBtns;
exports.createComp = createComp;
exports.createEl = createEl;
exports.createEvt = createEvt;
exports.createFooter = createFooter;
exports.createModule = createModule;
exports.createTools = createTools;
exports.curveFns = curveFns;
exports.dateTools = dateTools;
exports.debounce = debounce;
exports.decompTask = decompTask;
exports.deepClone = deepClone;
exports.deepEqual = deepEqual;
exports.deepMerge = deepMerge;
exports.default = ax_comm;
exports.delay = delay;
exports.dl2Tree = dl2Tree;
exports.ease = ease;
exports.easeHeight = easeHeight;
exports.elProps = elProps;
exports.elState = elState;
exports.elsSort = elsSort;
exports.eventMap = eventMap;
exports.events = events;
exports.extend = extend;
exports.fadeIn = fadeIn;
exports.fadeOut = fadeOut;
exports.fadeToggle = fadeToggle;
exports.fieldTools = fieldTools;
exports.fieldTypes = fieldTypes;
exports.fileTools = fileTools;
exports.filterPrims = filterPrims;
exports.findItem = findItem;
exports.findItems = findItems;
exports.formTools = formTools;
exports.getArrMap = getArrMap;
exports.getAttrArr = getAttrArr;
exports.getAttrBool = getAttrBool;
exports.getAutoDur = getAutoDur;
exports.getBetweenEls = getBetweenEls;
exports.getClasses = getClasses;
exports.getClientObj = getClientObj;
exports.getComputedVar = getComputedVar;
exports.getContent = getContent;
exports.getDataType = getDataType;
exports.getEl = getEl;
exports.getElSpace = getElSpace;
exports.getEls = getEls;
exports.getEvtTarget = getEvtTarget;
exports.getExpiration = getExpiration;
exports.getFullGap = getFullGap;
exports.getHeights = getHeights;
exports.getHypotenuse = getHypotenuse;
exports.getImgAvatar = getImgAvatar;
exports.getImgEmpty = getImgEmpty;
exports.getImgNone = getImgNone;
exports.getImgSpin = getImgSpin;
exports.getImgSpinDk = getImgSpinDk;
exports.getIntArr = getIntArr;
exports.getLast = getLast;
exports.getNestProp = getNestProp;
exports.getPlaces = getPlaces;
exports.getRectPoints = getRectPoints;
exports.getScreenSize = getScreenSize;
exports.getScrollObj = getScrollObj;
exports.getSelectorType = getSelectorType;
exports.getStrFromTpl = getStrFromTpl;
exports.getUTCTimestamp = getUTCTimestamp;
exports.getValsFromAttrs = getValsFromAttrs;
exports.getWidths = getWidths;
exports.hide = hide;
exports.icons = icons;
exports.includes = includes;
exports.increaseId = increaseId;
exports.init = init;
exports.instance = instance;
exports.isDateStr = isDateStr;
exports.isEmpty = isEmpty;
exports.isMobi = isMobi;
exports.isNull = isNull;
exports.isOutside = isOutside;
exports.isProxy = isProxy;
exports.isScrollUp = isScrollUp;
exports.isSubset = isSubset;
exports.keyCond = keyCond;
exports.moveItem = moveItem;
exports.notice = notice;
exports.offset = offset;
exports.paramToJson = paramToJson;
exports.parseStr = parseStr;
exports.parseUrlArr = parseUrlArr;
exports.pipe = pipe;
exports.plan = plan;
exports.prefix = prefix;
exports.preventDft = preventDft;
exports.privacy = privacy;
exports.prompt = prompt;
exports.propsMap = propsMap;
exports.purifyHtml = purifyHtml;
exports.regElem = regElem;
exports.regExps = regExps;
exports.removeItem = removeItem;
exports.removeStyle = removeStyle;
exports.removeStyles = removeStyles;
exports.renderTpl = renderTpl;
exports.repeatStr = repeatStr;
exports.replaceFrag = replaceFrag;
exports.requireTypes = requireTypes;
exports.scrollTo = scrollTo;
exports.select2Tree = select2Tree;
exports.setAttr = setAttr;
exports.setAttrs = setAttrs;
exports.setContent = setContent;
exports.setSingleSel = setSingleSel;
exports.show = show;
exports.sliceFrags = sliceFrags;
exports.sliceStrEnd = sliceStrEnd;
exports.slideDown = slideDown;
exports.slideToggle = slideToggle;
exports.slideUp = slideUp;
exports.splice = splice;
exports.splitNum = splitNum;
exports.spreadBool = spreadBool;
exports.startUpper = startUpper;
exports.stdParam = stdParam;
exports.storage = storage;
exports.strToJson = strToJson;
exports.style = style;
exports.support = support;
exports.theme = theme;
exports.throttle = throttle;
exports.toLocalTime = toLocalTime;
exports.toNumber = toNumber;
exports.toPixel = toPixel;
exports.toggle = toggle;
exports.tplToEl = tplToEl;
exports.tplToEls = tplToEls;
exports.transformTools = transformTools;
exports.treeTools = treeTools;
exports.trim = trim;
exports.ul2Tree = ul2Tree;
exports.unique = unique;
exports.valToArr = valToArr;
exports.validTools = validTools;
