UNPKG

@codady/axui

Version:

The AXUI front-end framework is built on HTML5, CSS3, and JavaScript standards, with TypeScript used for type management. It has no library dependencies and is designed to meet diverse needs with a focus on design.

1,255 lines (1,203 loc) 1.48 MB
/*! * @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 */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ax = factory()); })(this, (function () { 'use strict'; 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'; } }