mirror of
https://github.com/layui/layui.git
synced 2026-02-09 02:09:18 +08:00
1024 lines
27 KiB
JavaScript
1024 lines
27 KiB
JavaScript
/**
|
||
* Layui
|
||
* MIT Licensed
|
||
*/
|
||
|
||
// 便于打包时的字符压缩
|
||
var document = window.document;
|
||
var location = window.location;
|
||
|
||
// 基础配置
|
||
var config = {
|
||
timeout: 10,
|
||
// 符合规范的模块请求最长等待秒数
|
||
debug: false,
|
||
// 是否开启调试模式
|
||
version: false // 是否在模块请求时加入版本号参数(以更新模块缓存)
|
||
};
|
||
|
||
// 模块加载缓存信息
|
||
var cache = {
|
||
modules: {},
|
||
// 模块物理路径
|
||
status: {},
|
||
// 模块加载就绪状态
|
||
event: {},
|
||
// 模块自定义事件
|
||
callback: {} // 模块的回调
|
||
};
|
||
|
||
// constructor
|
||
var Class = function () {
|
||
this.v = '3.0.0-alpha.2'; // 版本号
|
||
};
|
||
|
||
// 识别预先可能定义的指定全局对象
|
||
var GLOBAL = window.LAYUI_GLOBAL || {};
|
||
|
||
// 获取 layui 所在目录
|
||
var getPath = function () {
|
||
var jsPath = document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' ? document.currentScript.src : function () {
|
||
var js = document.getElementsByTagName('script');
|
||
var last = js.length - 1;
|
||
var src;
|
||
for (var i = last; i > 0; i--) {
|
||
if (js[i].readyState === 'interactive') {
|
||
src = js[i].src;
|
||
break;
|
||
}
|
||
}
|
||
return src || js[last].src;
|
||
}();
|
||
return config.dir = GLOBAL.dir || jsPath.substring(0, jsPath.lastIndexOf('/') + 1);
|
||
}();
|
||
|
||
// 异常提示
|
||
var error = function (msg, type) {
|
||
type = type || 'warn';
|
||
msg = '[Layui warn]: ' + msg;
|
||
|
||
// 仅允许 error 或 warn 两种类型的提示
|
||
if (/warn|error/.test(type.trim())) {
|
||
type = 'warn';
|
||
}
|
||
window.console[type](msg);
|
||
};
|
||
var warned = Object.create(null);
|
||
var errorOnce = function (msg, type) {
|
||
if (warned._size && warned._size > 100) {
|
||
warned = Object.create(null);
|
||
warned._size = 0;
|
||
}
|
||
if (!warned[msg]) {
|
||
warned[msg] = true;
|
||
warned._size = (warned._size || 0) + 1;
|
||
error(msg, type);
|
||
}
|
||
};
|
||
|
||
// 内置模块
|
||
var builtinModules = config.builtin = {
|
||
lay: 'lay',
|
||
// 基础 DOM 操作
|
||
layer: 'layer',
|
||
// 弹层
|
||
laydate: 'laydate',
|
||
// 日期
|
||
laypage: 'laypage',
|
||
// 分页
|
||
laytpl: 'laytpl',
|
||
// 模板引擎
|
||
form: 'form',
|
||
// 表单集
|
||
upload: 'upload',
|
||
// 上传
|
||
dropdown: 'dropdown',
|
||
// 下拉菜单
|
||
transfer: 'transfer',
|
||
// 穿梭框
|
||
tree: 'tree',
|
||
// 树结构
|
||
table: 'table',
|
||
// 表格
|
||
treeTable: 'treeTable',
|
||
// 树表
|
||
tabs: 'tabs',
|
||
// 标签页
|
||
tab: 'tab',
|
||
// 选项卡(兼容保留)
|
||
nav: 'nav',
|
||
// 导航菜单
|
||
breadcrumb: 'breadcrumb',
|
||
// 面包屑
|
||
progress: 'progress',
|
||
// 进度条
|
||
collapse: 'collapse',
|
||
// 折叠面板
|
||
element: 'element',
|
||
// 常用元素操作
|
||
rate: 'rate',
|
||
// 评分组件
|
||
colorpicker: 'colorpicker',
|
||
// 颜色选择器
|
||
slider: 'slider',
|
||
// 滑块
|
||
carousel: 'carousel',
|
||
// 轮播
|
||
flow: 'flow',
|
||
// 流加载
|
||
util: 'util',
|
||
// 工具块
|
||
code: 'code',
|
||
// 代码修饰器
|
||
jquery: 'jquery',
|
||
// DOM 库(第三方)
|
||
component: 'component',
|
||
// 组件构建器
|
||
i18n: 'i18n',
|
||
// 国际化
|
||
|
||
all: 'all',
|
||
'layui.all': 'layui.all' // 聚合标识(功能性的,非真实模块)
|
||
};
|
||
|
||
/**
|
||
* 节点加载事件
|
||
* @param {HTMLElement} node - script 或 link 节点
|
||
* @param {Function} done
|
||
* @param {Function} error
|
||
*/
|
||
var onNodeLoad = function (node, done, error) {
|
||
// 资源加载完毕
|
||
var onCompleted = function (e) {
|
||
var readyRegExp = /^(complete|loaded)$/;
|
||
if (e.type === 'load' || readyRegExp.test((e.currentTarget || e.srcElement).readyState)) {
|
||
removeListener();
|
||
typeof done === 'function' && done(e);
|
||
}
|
||
};
|
||
// 资源加载失败
|
||
var onError = function (e) {
|
||
removeListener();
|
||
typeof error === 'function' && error(e);
|
||
};
|
||
|
||
// 移除事件
|
||
var removeListener = function () {
|
||
if (node.detachEvent) {
|
||
node.detachEvent('onreadystatechange', onCompleted);
|
||
} else {
|
||
node.removeEventListener('load', onCompleted, false);
|
||
node.removeEventListener('error', onError, false);
|
||
}
|
||
};
|
||
|
||
// 添加事件
|
||
if (node.attachEvent && !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0)) {
|
||
// 此处考虑到 IE9+ load 的稳定性,固仍然采用 onreadystatechange
|
||
node.attachEvent('onreadystatechange', onCompleted);
|
||
} else {
|
||
node.addEventListener('load', onCompleted, false);
|
||
node.addEventListener('error', onError, false);
|
||
}
|
||
};
|
||
|
||
// 获取配置及临时缓存信息
|
||
Class.prototype.cache = Object.assign(config, cache);
|
||
|
||
/**
|
||
* 全局配置
|
||
* @param {Object} options - 配置对象
|
||
*/
|
||
Class.prototype.config = function (options) {
|
||
Object.assign(config, options);
|
||
return this;
|
||
};
|
||
|
||
/**
|
||
* 定义模块
|
||
* @param {(string|string[])} deps - 依赖的模块列表
|
||
* @param {Function} callback - 模块的回调
|
||
*/
|
||
Class.prototype.define = function (deps, callback) {
|
||
var that = this;
|
||
var useCallback = function () {
|
||
var setModule = function (mod, exports$1) {
|
||
layui[mod] = exports$1; // 将模块接口赋值在 layui 对象中
|
||
cache.status[mod] = true; // 标记模块注册完成
|
||
};
|
||
// 执行模块的回调
|
||
typeof callback === 'function' && callback(function (mod, exports$1) {
|
||
setModule(mod, exports$1);
|
||
// 记录模块回调,以便需要时再执行
|
||
cache.callback[mod] = function () {
|
||
callback(setModule);
|
||
};
|
||
});
|
||
return this;
|
||
};
|
||
|
||
// 若未依赖模块
|
||
if (typeof deps === 'function') {
|
||
callback = deps;
|
||
deps = [];
|
||
}
|
||
that.use(deps, useCallback, null, 'define');
|
||
return that;
|
||
};
|
||
|
||
/**
|
||
* 使用模块
|
||
* @param {(string|string[])} mods - 模块列表
|
||
* @param {Function} callback - 回调
|
||
*/
|
||
Class.prototype.use = function (mods, callback, exports$1, from) {
|
||
var that = this;
|
||
var dir = config.dir = config.dir ? config.dir : getPath;
|
||
|
||
// 整理模块队列
|
||
mods = function () {
|
||
if (typeof mods === 'string') {
|
||
return [mods];
|
||
}
|
||
// 若第一个参数为 function ,则自动加载所有内置模块,且执行的回调即为该 function 参数;
|
||
else if (typeof mods === 'function') {
|
||
callback = mods;
|
||
return ['all'];
|
||
}
|
||
return mods;
|
||
}();
|
||
|
||
// 获取 layui 静态资源所在 host
|
||
if (!config.host) {
|
||
config.host = (dir.match(/\/\/([\s\S]+?)\//) || ['//' + location.host + '/'])[0];
|
||
}
|
||
|
||
// 若参数异常
|
||
if (!mods) return that;
|
||
|
||
// 若页面已经存在 jQuery 且所定义的模块依赖 jQuery,则不加载内部 jquery 模块
|
||
if (window.jQuery && window.jQuery.fn.on) {
|
||
that.each(mods, function (index, item) {
|
||
if (item === 'jquery') {
|
||
mods.splice(index, 1);
|
||
}
|
||
});
|
||
layui.jquery = layui.$ = window.jQuery;
|
||
}
|
||
|
||
// 将模块的接口作为回调的参数传递
|
||
exports$1 = exports$1 || [];
|
||
|
||
// 加载当前队列的第一个模块
|
||
var item = mods[0];
|
||
var modInfo = that.modules[item]; // 当前模块信息
|
||
// 是否为外部模块,即无需遵循 layui 轻量级模块规范的任意第三方组件。
|
||
var isExternalModule = typeof modInfo === 'object';
|
||
|
||
// 回调触发
|
||
var onCallback = function () {
|
||
exports$1.push(layui[item]);
|
||
mods.length > 1 ? that.use(mods.slice(1), callback, exports$1, from) : typeof callback === 'function' && function () {
|
||
// 保证文档加载完毕再执行回调
|
||
if (layui.jquery && typeof layui.jquery === 'function' && from !== 'define') {
|
||
return layui.jquery(function () {
|
||
callback.apply(layui, exports$1);
|
||
});
|
||
}
|
||
callback.apply(layui, exports$1);
|
||
}();
|
||
};
|
||
|
||
// 回调轮询
|
||
var pollCallback = function () {
|
||
var timeout = 0; // 超时计数器(秒)
|
||
var delay = 5; // 轮询等待毫秒数
|
||
|
||
// 轮询模块加载完毕状态
|
||
(function poll() {
|
||
if (++timeout > config.timeout * 1000 / delay) {
|
||
return error(item + ' is not a valid module', 'error');
|
||
}
|
||
|
||
// 根据模块加载完毕的标志来完成轮询
|
||
// 若为任意外部模块,则标志为该模块接口是否存在;
|
||
// 若为遵循 layui 规范的模块,则标志为模块的 status 状态值
|
||
(isExternalModule ? layui[item] = window[modInfo.api] : cache.status[item]) ? onCallback() : setTimeout(poll, delay);
|
||
})();
|
||
};
|
||
|
||
// 若为发行版,则内置模块不必异步加载
|
||
if (mods.length === 0 || layui['layui.all'] && builtinModules[item]) {
|
||
return onCallback(), that;
|
||
}
|
||
|
||
// 当前模块所在路径
|
||
var modSrc = isExternalModule ? modInfo.src : modInfo;
|
||
|
||
// 基础路径
|
||
var basePath = builtinModules[item] ? dir + 'modules/' // 若为内置模块,则按照默认 dir 参数拼接模块 URL
|
||
: modSrc ? '' : config.base; // 若为扩展模块,且模块路径已设置,则不必再重复拼接基础路径
|
||
|
||
// 若从 layui.modules 为获取到模块路径, 则将传入的模块名视为路径名
|
||
if (!modSrc) modSrc = item;
|
||
|
||
// 过滤空格符和 .js 后缀
|
||
modSrc = modSrc.replace(/\s/g, '').replace(/\.js[^/.]*$/, '');
|
||
|
||
// 拼接最终模块 URL
|
||
var url = basePath + modSrc + '.js';
|
||
|
||
// 若扩展模块对象已经存在,则不必再重复加载
|
||
if (!cache.modules[item] && layui[item]) {
|
||
cache.modules[item] = url; // 并记录起该扩展模块的 url
|
||
}
|
||
|
||
// 首次加载模块
|
||
if (!cache.modules[item]) {
|
||
var head = document.getElementsByTagName('head')[0];
|
||
var node = document.createElement('script');
|
||
node.async = true;
|
||
node.charset = 'utf-8'; // 避免 IE9 的编码问题
|
||
node.src = url + function () {
|
||
var version = config.version === true ? config.v || new Date().getTime() : config.version || '';
|
||
return version ? '?v=' + version : '';
|
||
}();
|
||
head.appendChild(node);
|
||
|
||
// 节点加载事件
|
||
onNodeLoad(node, function () {
|
||
head.removeChild(node);
|
||
pollCallback();
|
||
}, function () {
|
||
head.removeChild(node);
|
||
});
|
||
|
||
// 模块已首次加载的标记
|
||
cache.modules[item] = url;
|
||
} else {
|
||
// 再次 use 模块
|
||
pollCallback();
|
||
}
|
||
return that;
|
||
};
|
||
|
||
// 记录全部模块
|
||
Class.prototype.modules = Object.assign({}, builtinModules);
|
||
|
||
/**
|
||
* 拓展模块
|
||
* @param {Object} settings - 拓展模块的配置
|
||
*/
|
||
Class.prototype.extend = function (settings) {
|
||
var that = this;
|
||
var base = config.base || '';
|
||
var firstSymbolEXP = /^\{\/\}/; // 模块单独路径首字符表达式
|
||
|
||
settings = settings || {};
|
||
|
||
// 遍历拓展模块
|
||
for (var modName in settings) {
|
||
if (that[modName] || that.modules[modName]) {
|
||
// 验证模块是否被占用
|
||
error('the ' + modName + ' module already exists, extend failure');
|
||
} else {
|
||
var modInfo = settings[modName];
|
||
// 若直接传入模块路径字符
|
||
if (typeof modInfo === 'string') {
|
||
// 判断传入的模块路径是否特定首字符
|
||
// 若存在特定首字符,则模块 URL 即为该首字符后面紧跟的字符
|
||
// 否则,则按照 config.base 路径进行拼接
|
||
if (firstSymbolEXP.test(modInfo)) base = '';
|
||
modInfo = (base + modInfo).replace(firstSymbolEXP, '');
|
||
}
|
||
that.modules[modName] = modInfo;
|
||
}
|
||
}
|
||
return that;
|
||
};
|
||
|
||
/**
|
||
* 弃用指定的模块,以便重新扩展新的同名模块。
|
||
* @param {(string|string[])} mods - 模块列表
|
||
*/
|
||
Class.prototype.disuse = function (mods) {
|
||
var that = this;
|
||
mods = that.isArray(mods) ? mods : [mods];
|
||
that.each(mods, function (index, item) {
|
||
delete that[item];
|
||
delete builtinModules[item];
|
||
delete that.modules[item];
|
||
delete cache.status[item];
|
||
delete cache.modules[item];
|
||
});
|
||
return that;
|
||
};
|
||
|
||
/**
|
||
* 获取节点的 style 属性值
|
||
* currentStyle.getAttribute 参数为 camelCase 形式的字符串
|
||
* @param {HTMLElement} node - 节点
|
||
* @param {string} name - 属性名
|
||
* @returns 属性值
|
||
*/
|
||
Class.prototype.getStyle = function (node, name) {
|
||
var style = node.currentStyle ? node.currentStyle : window.getComputedStyle(node, null);
|
||
return style.getPropertyValue ? style.getPropertyValue(name) : style.getAttribute(name.replace(/-(\w)/g, function (_, c) {
|
||
return c ? c.toUpperCase() : '';
|
||
}));
|
||
};
|
||
|
||
/**
|
||
* CSS 外部加载器
|
||
* @param {string} href - 外部 CSS 文件路径
|
||
* @param {Function} callback - 回调函数
|
||
* @param {string} id - 定义 link 标签的 id
|
||
*/
|
||
Class.prototype.link = function (href, callback, id) {
|
||
var that = this;
|
||
var head = document.getElementsByTagName('head')[0];
|
||
var hasCallback = typeof callback === 'function';
|
||
|
||
// 若第二个参数为 string 类型,则该参数为 id
|
||
if (typeof callback === 'string') {
|
||
id = callback;
|
||
}
|
||
|
||
// 若加载多个
|
||
if (typeof href === 'object') {
|
||
var isArr = that.type(id) === 'array';
|
||
return that.each(href, function (index, value) {
|
||
that.link(value, index === href.length - 1 && callback, isArr && id[index]);
|
||
});
|
||
}
|
||
|
||
// 若未传入 id ,则取路径 `//` 后面的字符拼接为 id,不含.与参数
|
||
id = id || href.replace(/^(#|(http(s?)):\/\/|\/\/)|\.|\/|\?.+/g, '');
|
||
id = 'layuicss-' + id;
|
||
var link = document.getElementById(id);
|
||
|
||
// 初始创建节点
|
||
if (!link) {
|
||
link = document.createElement('link');
|
||
link.href = href + (config.debug ? '?v=' + new Date().getTime() : '');
|
||
link.rel = 'stylesheet';
|
||
link.id = id;
|
||
head.appendChild(link);
|
||
}
|
||
|
||
// 若加载已完成,则直接执行回调函数
|
||
if (link.__lay_readyState__ === 'complete') {
|
||
hasCallback && callback(link);
|
||
return that;
|
||
}
|
||
|
||
// 初始加载
|
||
onNodeLoad(link, function () {
|
||
link.__lay_readyState__ = 'complete';
|
||
hasCallback && callback(link);
|
||
}, function () {
|
||
error(href + ' load error', 'error');
|
||
head.removeChild(link); // 移除节点
|
||
});
|
||
return that;
|
||
};
|
||
|
||
/**
|
||
* CSS 内部加载器
|
||
* @param {string} modName - 模块名
|
||
*/
|
||
Class.prototype.addcss = function (modName, callback, id) {
|
||
return layui.link(config.dir + 'css/' + modName, callback, id);
|
||
};
|
||
|
||
/**
|
||
* 获取执行定义模块时的回调函数,factory 为向下兼容
|
||
* @param {string} modName - 模块名
|
||
* @returns {Function}
|
||
*/
|
||
Class.prototype.factory = function (modName) {
|
||
if (layui[modName]) {
|
||
return typeof config.callback[modName] === 'function' ? config.callback[modName] : null;
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 图片预加载
|
||
* @param {string} url - 图片路径
|
||
* @param {Function} callback - 成功回调
|
||
* @param {Function} error - 错误回调
|
||
*/
|
||
Class.prototype.img = function (url, callback, error) {
|
||
var img = new Image();
|
||
img.src = url;
|
||
if (img.complete) {
|
||
return callback(img);
|
||
}
|
||
img.onload = function () {
|
||
img.onload = null;
|
||
typeof callback === 'function' && callback(img);
|
||
};
|
||
img.onerror = function (e) {
|
||
img.onerror = null;
|
||
typeof error === 'function' && error(e);
|
||
};
|
||
};
|
||
|
||
/**
|
||
* location.hash 路由解析
|
||
* @param {string} hash 值
|
||
* @returns {Object}
|
||
*/
|
||
Class.prototype.router = Class.prototype.hash = function (hash) {
|
||
hash = hash || location.hash;
|
||
var that = this;
|
||
var data = {
|
||
path: [],
|
||
pathname: [],
|
||
search: {},
|
||
hash: (hash.match(/[^#](#.*$)/) || [])[1] || '',
|
||
href: ''
|
||
};
|
||
|
||
// 禁止非 hash 路由规范
|
||
if (!/^#/.test(hash)) {
|
||
return data;
|
||
}
|
||
hash = hash.replace(/^#/, '');
|
||
data.href = hash;
|
||
hash = hash.replace(/([^#])(#.*$)/, '$1').split('/') || [];
|
||
|
||
// 提取 Hash 结构
|
||
that.each(hash, function (index, item) {
|
||
/^\w+=/.test(item) ? function () {
|
||
item = item.split('=');
|
||
data.search[item[0]] = item[1];
|
||
}() : data.path.push(item);
|
||
});
|
||
data.pathname = data.path; // path → pathname, 与 layui.url 一致
|
||
return data;
|
||
};
|
||
|
||
/**
|
||
* URL 解析
|
||
* @param {string} href - url 路径
|
||
* @returns {Object}
|
||
*/
|
||
Class.prototype.url = function (href) {
|
||
var that = this;
|
||
var data = {
|
||
// 提取 url 路径
|
||
pathname: function () {
|
||
var pathname = href ? function () {
|
||
var str = (href.match(/\.[^.]+?\/.+/) || [])[0] || '';
|
||
return str.replace(/^[^/]+/, '').replace(/\?.+/, '');
|
||
}() : location.pathname;
|
||
return pathname.replace(/^\//, '').split('/');
|
||
}(),
|
||
// 提取 url 参数
|
||
search: function () {
|
||
var obj = {};
|
||
var search = (href ? function () {
|
||
var str = (href.match(/\?.+/) || [])[0] || '';
|
||
return str.replace(/#.+/, '');
|
||
}() : location.search).replace(/^\?+/, '').split('&'); // 去除 ?,按 & 分割参数
|
||
|
||
// 遍历分割后的参数
|
||
that.each(search, function (index, item) {
|
||
var _index = item.indexOf('=');
|
||
var key = function () {
|
||
// 提取 key
|
||
if (_index < 0) {
|
||
return item.substr(0, item.length);
|
||
} else if (_index === 0) {
|
||
return false;
|
||
} else {
|
||
return item.substr(0, _index);
|
||
}
|
||
}();
|
||
// 提取 value
|
||
if (key) {
|
||
obj[key] = _index > 0 ? item.substr(_index + 1) : null;
|
||
}
|
||
});
|
||
return obj;
|
||
}(),
|
||
// 提取 Hash
|
||
hash: that.router(function () {
|
||
return href ? (href.match(/#.+/) || [])[0] || '/' : location.hash;
|
||
}())
|
||
};
|
||
return data;
|
||
};
|
||
|
||
/**
|
||
* 本地持久存储
|
||
* @param {string} table - 表名
|
||
* @param {Object} settings - 设置项
|
||
* @param {Storage} storage - 存储对象,localStorage 或 sessionStorage
|
||
* @returns {Object}
|
||
*/
|
||
Class.prototype.data = function (table, settings, storage) {
|
||
table = table || 'layui';
|
||
storage = storage || localStorage;
|
||
|
||
// 如果 settings 为 null,则删除表
|
||
if (settings === null) {
|
||
return delete storage[table];
|
||
}
|
||
settings = typeof settings === 'object' ? settings : {
|
||
key: settings
|
||
};
|
||
var data;
|
||
try {
|
||
data = JSON.parse(storage[table]);
|
||
} catch {
|
||
data = {};
|
||
}
|
||
if ('value' in settings) data[settings.key] = settings.value;
|
||
if (settings.remove) delete data[settings.key];
|
||
storage[table] = JSON.stringify(data);
|
||
return settings.key ? data[settings.key] : data;
|
||
};
|
||
|
||
/**
|
||
* 本地临时存储
|
||
* @param {string} table - 表名
|
||
* @param {Object} settings - 设置项
|
||
* @returns {Object}
|
||
*/
|
||
Class.prototype.sessionData = function (table, settings) {
|
||
return this.data(table, settings, sessionStorage);
|
||
};
|
||
|
||
/**
|
||
* 设备信息
|
||
* @param {string} key - 任意 key
|
||
* @returns {Object}
|
||
*/
|
||
Class.prototype.device = function (key) {
|
||
var agent = navigator.userAgent.toLowerCase();
|
||
|
||
// 获取版本号
|
||
var getVersion = function (label) {
|
||
var exp = new RegExp(label + '/([^\\s\\_\\-]+)');
|
||
label = (agent.match(exp) || [])[1];
|
||
return label || false;
|
||
};
|
||
|
||
// 返回结果集
|
||
var result = {
|
||
os: function () {
|
||
// 底层操作系统
|
||
if (/windows/.test(agent)) {
|
||
return 'windows';
|
||
} else if (/linux/.test(agent)) {
|
||
return 'linux';
|
||
} else if (/iphone|ipod|ipad|ios/.test(agent)) {
|
||
return 'ios';
|
||
} else if (/mac/.test(agent)) {
|
||
return 'mac';
|
||
}
|
||
}(),
|
||
ie: function () {
|
||
// ie 版本
|
||
return !!window.ActiveXObject || 'ActiveXObject' in window ? (agent.match(/msie\s(\d+)/) || [])[1] || '11' // 由于 ie11 并没有 msie 的标识
|
||
: false;
|
||
}(),
|
||
weixin: getVersion('micromessenger') // 是否微信
|
||
};
|
||
|
||
// 任意的 key
|
||
if (key && !result[key]) {
|
||
result[key] = getVersion(key);
|
||
}
|
||
|
||
// 移动设备
|
||
result.android = /android/.test(agent);
|
||
result.ios = result.os === 'ios';
|
||
result.mobile = result.android || result.ios;
|
||
return result;
|
||
};
|
||
|
||
// 提示
|
||
Class.prototype.hint = function () {
|
||
return {
|
||
error: error,
|
||
errorOnce: errorOnce
|
||
};
|
||
};
|
||
|
||
/**
|
||
* typeof 类型细分 -> string/number/boolean/undefined/null、object/array/function/…
|
||
* @param {*} operand - 任意值
|
||
* @returns {string}
|
||
*/
|
||
Class.prototype._typeof = Class.prototype.type = function (operand) {
|
||
if (operand === null) return String(operand);
|
||
|
||
// 细分引用类型
|
||
return typeof operand === 'object' || typeof operand === 'function' ? function () {
|
||
var type = Object.prototype.toString.call(operand).match(/\s(.+)\]$/) || []; // 匹配类型字符
|
||
var classType = 'Function|Array|Date|RegExp|Object|Error|Symbol'; // 常见类型字符
|
||
|
||
type = type[1] || 'Object';
|
||
|
||
// 除匹配到的类型外,其他对象均返回 object
|
||
return new RegExp('\\b(' + classType + ')\\b').test(type) ? type.toLowerCase() : 'object';
|
||
}() : typeof operand;
|
||
};
|
||
|
||
/**
|
||
* 对象是否具备数组结构(此处为兼容 jQuery 对象)
|
||
* @param {Object} obj - 任意对象
|
||
* @returns {boolean}
|
||
*/
|
||
Class.prototype._isArray = Class.prototype.isArray = function (obj) {
|
||
var that = this;
|
||
var len;
|
||
var type = that.type(obj);
|
||
if (!obj || typeof obj !== 'object' || obj === window) return false;
|
||
len = 'length' in obj && obj.length; // 兼容 ie
|
||
return type === 'array' || len === 0 || typeof len === 'number' && len > 0 && len - 1 in obj // 兼容 jQuery 对象
|
||
;
|
||
};
|
||
|
||
/**
|
||
* 遍历
|
||
* @param {Object} obj - 任意对象
|
||
* @param {Function} fn - 遍历回调
|
||
*/
|
||
Class.prototype.each = function (obj, fn) {
|
||
var key;
|
||
var that = this;
|
||
var callback = function (key, obj) {
|
||
return fn.call(obj[key], key, obj[key]);
|
||
};
|
||
if (typeof fn !== 'function') {
|
||
return that;
|
||
}
|
||
obj = obj || [];
|
||
|
||
// 优先处理数组结构
|
||
if (that.isArray(obj)) {
|
||
for (key = 0; key < obj.length; key++) {
|
||
if (callback(key, obj)) break;
|
||
}
|
||
} else {
|
||
for (key in obj) {
|
||
if (callback(key, obj)) break;
|
||
}
|
||
}
|
||
return that;
|
||
};
|
||
|
||
/**
|
||
* 将数组中的成员对象按照某个 key 的 value 值进行排序
|
||
* @param {Object[]} arr - 任意数组
|
||
* @param {string} key - 任意 key
|
||
* @param {boolean} desc - 是否降序
|
||
* @param {boolean} notClone - 是否不对 arr 进行克隆
|
||
* @returns {Object[]}
|
||
*/
|
||
Class.prototype.sort = function (arr, key, desc, notClone) {
|
||
var that = this;
|
||
var clone = notClone ? arr || [] : JSON.parse(JSON.stringify(arr || []));
|
||
|
||
// 若未传入 key,则直接返回原对象
|
||
if (that.type(arr) === 'object' && !key) {
|
||
return clone;
|
||
} else if (typeof arr !== 'object') {
|
||
// 若 arr 非对象
|
||
return [clone];
|
||
}
|
||
|
||
// 开始排序
|
||
clone.sort(function (o1, o2) {
|
||
var v1 = o1[key];
|
||
var v2 = o2[key];
|
||
|
||
/*
|
||
* 特殊数据
|
||
* 若比较的成员均非对象
|
||
*/
|
||
|
||
// 若比较的成员均为数字
|
||
if (!isNaN(o1) && !isNaN(o2)) return o1 - o2;
|
||
|
||
// 若比较的成员只存在某一个非对象
|
||
if (!isNaN(o1) && isNaN(o2)) {
|
||
if (key && typeof o2 === 'object') {
|
||
v1 = o1;
|
||
} else {
|
||
return -1;
|
||
}
|
||
} else if (isNaN(o1) && !isNaN(o2)) {
|
||
if (key && typeof o1 === 'object') {
|
||
v2 = o2;
|
||
} else {
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 正常数据
|
||
* 即成员均为对象,也传入了对比依据: key
|
||
* 若 value 为数字,按「大小」排序;若 value 非数字,则按「字典序」排序
|
||
*/
|
||
|
||
// value 是否为数字
|
||
var isNum = [!isNaN(v1), !isNaN(v2)];
|
||
|
||
// 若为数字比较
|
||
if (isNum[0] && isNum[1]) {
|
||
if (v1 && !v2 && v2 !== 0) {
|
||
// 数字 vs 空
|
||
return 1;
|
||
} else if (!v1 && v1 !== 0 && v2) {
|
||
// 空 vs 数字
|
||
return -1;
|
||
} else {
|
||
// 数字 vs 数字
|
||
return v1 - v2;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 字典序排序
|
||
*/
|
||
|
||
// 若为非数字比较
|
||
if (!isNum[0] && !isNum[1]) {
|
||
// 字典序比较
|
||
if (v1 > v2) {
|
||
return 1;
|
||
} else if (v1 < v2) {
|
||
return -1;
|
||
} else {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
// 若为混合比较
|
||
if (isNum[0] || !isNum[1]) {
|
||
// 数字 vs 非数字
|
||
return -1;
|
||
} else if (!isNum[0] || isNum[1]) {
|
||
// 非数字 vs 数字
|
||
return 1;
|
||
}
|
||
});
|
||
desc && clone.reverse(); // 倒序
|
||
return clone;
|
||
};
|
||
|
||
/**
|
||
* 阻止事件冒泡
|
||
* @param {Event} thisEvent - 事件对象
|
||
*/
|
||
Class.prototype.stope = function (thisEvent) {
|
||
try {
|
||
thisEvent.stopPropagation();
|
||
} catch {
|
||
thisEvent.cancelBubble = true;
|
||
}
|
||
};
|
||
|
||
// 字符常理
|
||
var EV_REMOVE = 'LAYUI-EVENT-REMOVE';
|
||
|
||
/**
|
||
* 自定义模块事件
|
||
* @param {string} modName - 模块名
|
||
* @param {string} events - 事件名
|
||
* @param {Function} callback - 回调
|
||
* @returns {Object}
|
||
*/
|
||
Class.prototype.onevent = function (modName, events, callback) {
|
||
if (typeof modName !== 'string' || typeof callback !== 'function') {
|
||
return this;
|
||
}
|
||
return Class.event(modName, events, null, callback);
|
||
};
|
||
|
||
/**
|
||
* 执行自定义模块事件
|
||
* @param {string} modName - 模块名
|
||
* @param {string} events - 事件名
|
||
* @param {Object} params - 参数
|
||
* @param {Function} fn - 回调
|
||
*/
|
||
Class.prototype.event = Class.event = function (modName, events, params, fn) {
|
||
var that = this;
|
||
var result = null;
|
||
var filter = (events || '').match(/\((.*)\)$/) || []; // 提取事件过滤器字符结构,如:select(xxx)
|
||
var eventName = (modName + '.' + events).replace(filter[0], ''); // 获取事件名称,如:form.select
|
||
var filterName = filter[1] || ''; // 获取过滤器名称, 如:xxx
|
||
var callback = function (_, item) {
|
||
var res = item && item.call(that, params);
|
||
res === false && result === null && (result = false);
|
||
};
|
||
|
||
// 如果参数传入特定字符,则执行移除事件
|
||
if (params === EV_REMOVE) {
|
||
delete (that.cache.event[eventName] || {})[filterName];
|
||
return that;
|
||
}
|
||
|
||
// 添加事件
|
||
if (fn) {
|
||
cache.event[eventName] = cache.event[eventName] || {};
|
||
if (filterName) {
|
||
// 带 filter 不支持重复事件
|
||
cache.event[eventName][filterName] = [fn];
|
||
} else {
|
||
// 不带 filter 处理的是所有的同类事件,应该支持重复事件
|
||
cache.event[eventName][filterName] = cache.event[eventName][filterName] || [];
|
||
cache.event[eventName][filterName].push(fn);
|
||
}
|
||
return this;
|
||
}
|
||
|
||
// 执行事件回调
|
||
layui.each(cache.event[eventName], function (key, item) {
|
||
// 执行当前模块的全部事件
|
||
if (filterName === '{*}') {
|
||
layui.each(item, callback);
|
||
return;
|
||
}
|
||
|
||
// 执行指定事件
|
||
key === '' && layui.each(item, callback);
|
||
filterName && key === filterName && layui.each(item, callback);
|
||
});
|
||
return result;
|
||
};
|
||
|
||
/**
|
||
* 新增模块事件
|
||
* @param {string} events - 事件名
|
||
* @param {string} modName - 模块名
|
||
* @param {Function} callback - 回调
|
||
* @returns {Object}
|
||
*/
|
||
Class.prototype.on = function (events, modName, callback) {
|
||
var that = this;
|
||
return that.onevent.call(that, modName, events, callback);
|
||
};
|
||
|
||
/**
|
||
* 移除模块事件
|
||
* @param {string} events - 事件名
|
||
* @param {string} modName - 模块名
|
||
* @returns {Object}
|
||
*/
|
||
Class.prototype.off = function (events, modName) {
|
||
var that = this;
|
||
return that.event.call(that, modName, events, EV_REMOVE);
|
||
};
|
||
|
||
/**
|
||
* 防抖
|
||
* @param {Function} func - 回调
|
||
* @param {number} wait - 延时执行的毫秒数
|
||
* @returns {Function}
|
||
*/
|
||
Class.prototype.debounce = function (func, wait) {
|
||
var timeout;
|
||
return function () {
|
||
var context = this;
|
||
var args = arguments;
|
||
clearTimeout(timeout);
|
||
timeout = setTimeout(function () {
|
||
func.apply(context, args);
|
||
}, wait);
|
||
};
|
||
};
|
||
|
||
/**
|
||
* 节流
|
||
* @param {Function} func - 回调
|
||
* @param {number} wait - 不重复执行的毫秒数
|
||
*/
|
||
Class.prototype.throttle = function (func, wait) {
|
||
var cooldown = false;
|
||
return function () {
|
||
var context = this;
|
||
var args = arguments;
|
||
if (!cooldown) {
|
||
func.apply(context, args);
|
||
cooldown = true;
|
||
setTimeout(function () {
|
||
cooldown = false;
|
||
}, wait);
|
||
}
|
||
};
|
||
};
|
||
const layui = new Class();
|
||
|
||
// 阻止 layui.use 加载内部模块
|
||
layui.all = true;
|
||
layui['layui.all'] = 'layui.all';
|
||
|
||
export { layui as default, layui };
|