layui/src/layui.js

1025 lines
28 KiB
JavaScript
Raw Normal View History

2022-05-18 22:35:13 +08:00
/**
2021-05-18 02:42:31 +08:00
* Layui
2022-05-24 18:19:06 +08:00
* Classic modular front-end UI library
2021-05-08 06:31:19 +08:00
* MIT Licensed
2017-08-21 08:51:13 +08:00
*/
2023-11-14 22:14:19 +08:00
(function(window) {
'use strict';
2017-08-21 08:51:13 +08:00
// 便于打包时的字符压缩
var document = window.document;
var location = window.location;
// 基础配置
2023-05-04 10:17:04 +08:00
var config = {
timeout: 10, // 符合规范的模块请求最长等待秒数
debug: false, // 是否开启调试模式
version: false // 是否在模块请求时加入版本号参数(以更新模块缓存)
};
// 模块加载缓存信息
var cache = {
modules: {}, // 模块物理路径
status: {}, // 模块加载就绪状态
event: {}, // 模块自定义事件
callback: {} // 模块的回调
2023-05-04 10:17:04 +08:00
};
2017-08-21 08:51:13 +08:00
// constructor
var Class = function() {
2025-04-15 14:59:09 +08:00
this.v = '2.11.0-rc.3'; // 版本号
2023-05-04 10:17:04 +08:00
};
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
// 识别预先可能定义的指定全局对象
var GLOBAL = window.LAYUI_GLOBAL || {};
2017-08-21 08:51:13 +08:00
2023-04-30 01:10:55 +08:00
// 获取 layui 所在目录
var getPath = function() {
var jsPath = (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') ? document.currentScript.src : function(){
var js = document.getElementsByTagName('script');
2023-05-04 10:17:04 +08:00
var last = js.length - 1;
var src;
2017-11-16 07:34:22 +08:00
for(var i = last; i > 0; i--){
if(js[i].readyState === 'interactive'){
src = js[i].src;
break;
}
}
return src || js[last].src;
}();
2023-11-14 22:14:19 +08:00
2021-05-08 06:31:19 +08:00
return config.dir = GLOBAL.dir || jsPath.substring(0, jsPath.lastIndexOf('/') + 1);
2023-05-04 10:17:04 +08:00
}();
2017-08-21 08:51:13 +08:00
2023-04-30 01:10:55 +08:00
// 异常提示
var error = function(msg, type) {
2021-03-31 14:07:23 +08:00
type = type || 'log';
window.console && console[type] && console[type]('layui error hint: ' + msg);
2023-05-04 10:17:04 +08:00
};
2017-08-21 08:51:13 +08:00
2023-04-30 01:10:55 +08:00
// 内置模块
var builtinModules = config.builtin = {
2023-05-04 10:17:04 +08:00
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', // 树表
2025-01-20 10:25:36 +08:00
tabs: 'tabs', // 标签页
2023-05-04 10:17:04 +08:00
element: 'element', // 常用元素操作
rate: 'rate', // 评分组件
colorpicker: 'colorpicker', // 颜色选择器
slider: 'slider', // 滑块
carousel: 'carousel', // 轮播
flow: 'flow', // 流加载
util: 'util', // 工具块
code: 'code', // 代码修饰器
jquery: 'jquery', // DOM 库(第三方)
2025-01-20 10:25:36 +08:00
component: 'component', // 组件构建器
2023-11-14 22:14:19 +08:00
2023-05-04 10:17:04 +08:00
all: 'all',
'layui.all': 'layui.all' // 聚合标识(功能性的,非真实模块)
2017-08-21 08:51:13 +08:00
};
/**
* 低版本浏览器适配
* @see polyfill
*/
// Object.assign
if (typeof Object.assign !== 'function') {
Object.assign = function(target) {
var to = Object(target);
if (arguments.length < 2) return to;
var sourcesIndex = 1;
for (; sourcesIndex < arguments.length; sourcesIndex++) {
var nextSource = arguments[sourcesIndex];
if (!(nextSource === undefined || nextSource === null)) {
for (var nextKey in nextSource) {
// 确保属性是源对象自身的(而非来自继承)
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
};
}
/**
* 节点加载事件
* @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;
};
2017-08-21 08:51:13 +08:00
/**
* 定义模块
* @param {(string|string[])} deps - 依赖的模块列表
* @param {Function} callback - 模块的回调
*/
Class.prototype.define = function(deps, callback) {
2023-05-04 10:17:04 +08:00
var that = this;
var useCallback = function() {
var setModule = function(mod, exports) {
layui[mod] = exports; // 将模块接口赋值在 layui 对象中
cache.status[mod] = true; // 标记模块注册完成
2018-01-03 09:55:45 +08:00
};
// 执行模块的回调
typeof callback === 'function' && callback(function(mod, exports) {
setModule(mod, exports);
// 记录模块回调,以便需要时再执行
cache.callback[mod] = function() {
callback(setModule);
2018-01-03 09:55:45 +08:00
}
2017-08-21 08:51:13 +08:00
});
return this;
};
2023-11-14 22:14:19 +08:00
// 若未依赖模块
if (typeof deps === 'function') {
callback = deps;
deps = [];
}
2023-11-14 22:14:19 +08:00
that.use(deps, useCallback, null, 'define');
2017-08-21 08:51:13 +08:00
return that;
};
/**
* 使用模块
* @param {(string|string[])} mods - 模块列表
* @param {Function} callback - 回调
*/
Class.prototype.use = function(mods, callback, exports, from) {
2023-05-04 10:17:04 +08:00
var that = this;
var dir = config.dir = config.dir ? config.dir : getPath;
2017-08-21 08:51:13 +08:00
// 整理模块队列
mods = (function() {
if (typeof mods === 'string') {
return [mods];
2023-11-14 22:14:19 +08:00
}
// 若第一个参数为 function ,则自动加载所有内置模块,且执行的回调即为该 function 参数;
else if(typeof mods === 'function') {
callback = mods;
2021-04-05 23:41:30 +08:00
return ['all'];
2023-11-14 22:14:19 +08:00
}
return mods;
})();
// 获取 layui 静态资源所在 host
if (!config.host) {
config.host = (dir.match(/\/\/([\s\S]+?)\//)||['//'+ location.host +'/'])[0];
}
// 若参数异常
if (!mods) return that;
2023-11-14 22:14:19 +08:00
// 若页面已经存在 jQuery 且所定义的模块依赖 jQuery则不加载内部 jquery 模块
if (window.jQuery && jQuery.fn.on) {
that.each(mods, function(index, item) {
if (item === 'jquery') {
mods.splice(index, 1);
2017-08-21 08:51:13 +08:00
}
});
layui.jquery = layui.$ = window.jQuery;
2017-08-21 08:51:13 +08:00
}
2023-11-14 22:14:19 +08:00
// 将模块的接口作为回调的参数传递
2017-08-21 08:51:13 +08:00
exports = exports || [];
// 加载当前队列的第一个模块
var item = mods[0];
var modInfo = that.modules[item]; // 当前模块信息
// 是否为外部模块,即无需遵循 layui 轻量级模块规范的任意第三方组件。
var isExternalModule = typeof modInfo === 'object';
2023-11-14 22:14:19 +08:00
// 回调触发
var onCallback = function () {
2017-08-21 08:51:13 +08:00
exports.push(layui[item]);
mods.length > 1
? that.use(mods.slice(1), callback, exports, from)
: (typeof callback === 'function' && function() {
2023-04-30 01:10:55 +08:00
// 保证文档加载完毕再执行回调
2021-04-06 16:01:23 +08:00
if(layui.jquery && typeof layui.jquery === 'function' && from !== 'define'){
2021-04-05 23:41:30 +08:00
return layui.jquery(function(){
callback.apply(layui, exports);
});
}
callback.apply(layui, exports);
}() );
};
2023-11-14 22:14:19 +08:00
// 回调轮询
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])) {
2017-08-21 08:51:13 +08:00
return onCallback(), that;
}
2023-11-14 22:14:19 +08:00
// 当前模块所在路径
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
2020-01-15 06:30:00 +08:00
}
2017-08-21 08:51:13 +08:00
2023-04-30 01:10:55 +08:00
// 首次加载模块
if (!cache.modules[item]) {
var head = document.getElementsByTagName('head')[0];
var node = document.createElement('script');
2023-11-14 22:14:19 +08:00
2017-08-21 08:51:13 +08:00
node.async = true;
node.charset = 'utf-8'; // 避免 IE9 的编码问题
node.src = url + function() {
2023-11-14 22:14:19 +08:00
var version = config.version === true
? (config.v || (new Date()).getTime())
: (config.version || '');
2017-08-21 08:51:13 +08:00
return version ? ('?v=' + version) : '';
}();
2023-11-14 22:14:19 +08:00
2017-08-21 08:51:13 +08:00
head.appendChild(node);
2023-11-14 22:14:19 +08:00
// 节点加载事件
onNodeLoad(node, function() {
head.removeChild(node);
pollCallback();
}, function() {
head.removeChild(node);
});
2023-11-14 22:14:19 +08:00
// 模块已首次加载的标记
cache.modules[item] = url;
} else { // 再次 use 模块
pollCallback();
2017-08-21 08:51:13 +08:00
}
2023-11-14 22:14:19 +08:00
2017-08-21 08:51:13 +08:00
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];
2022-06-21 00:25:07 +08:00
});
2022-06-29 08:22:26 +08:00
return that;
2022-06-21 00:25:07 +08:00
};
/**
* 获取节点的 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);
2025-01-20 10:25:36 +08:00
return style.getPropertyValue
? style.getPropertyValue(name)
: style.getAttribute(name.replace(/-(\w)/g, function(_, c){ return c ? c.toUpperCase() : '';}));
2017-08-21 08:51:13 +08:00
};
/**
* CSS 外部加载器
* @param {string} href - 外部 CSS 文件路径
* @param {Function} callback - 回调函数
* @param {string} id - 定义 link 标签的 id
*/
Class.prototype.link = function(href, callback, id) {
2023-05-04 10:17:04 +08:00
var that = this;
var head = document.getElementsByTagName('head')[0];
var link = document.createElement('link');
2023-11-14 22:14:19 +08:00
// 若第二个参数为 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]
);
});
}
2023-11-14 22:14:19 +08:00
// 若为传入 id ,则取路径 `//` 后面的字符拼接为 id不含.与参数
id = id || href.replace(/^(#|(http(s?)):\/\/|\/\/)|\.|\/|\?.+/g, '');
id = 'layuicss-'+ id;
2023-11-14 22:14:19 +08:00
2017-08-21 08:51:13 +08:00
link.href = href + (config.debug ? '?v='+new Date().getTime() : '');
link.rel = 'stylesheet';
link.id = id;
2023-11-14 22:14:19 +08:00
// 插入节点
if (!document.getElementById(id)) {
2017-08-21 08:51:13 +08:00
head.appendChild(link);
}
2023-11-14 22:14:19 +08:00
// 是否执行回调
if (typeof callback !== 'function') {
return that;
}
2023-11-14 22:14:19 +08:00
onNodeLoad(link, function() {
callback(link);
}, function() {
error(href + ' load error', 'error');
head.removeChild(link); // 移除节点
});
2023-11-14 22:14:19 +08:00
2017-08-21 08:51:13 +08:00
return that;
};
2023-11-14 22:14:19 +08:00
/**
* CSS 内部加载器
* @param {string} modName - 模块名
*/
Class.prototype.addcss = function(modName, callback, id) {
return layui.link(config.dir + 'css/' + modName, callback, id);
2021-04-22 10:22:45 +08:00
};
2023-11-14 22:14:19 +08:00
/**
* 获取执行定义模块时的回调函数factory 为向下兼容
* @param {string} modName - 模块名
* @returns {Function}
*/
Class.prototype.factory = function(modName) {
if (layui[modName]) {
2023-11-14 22:14:19 +08:00
return typeof config.callback[modName] === 'function'
2018-01-03 09:55:45 +08:00
? config.callback[modName]
: null;
}
};
2017-08-21 08:51:13 +08:00
/**
* 图片预加载
* @param {string} url - 图片路径
* @param {Function} callback - 成功回调
* @param {Function} error - 错误回调
*/
Class.prototype.img = function(url, callback, error) {
2017-08-21 08:51:13 +08:00
var img = new Image();
2023-11-14 22:14:19 +08:00
img.src = url;
if (img.complete) {
2017-08-21 08:51:13 +08:00
return callback(img);
}
img.onload = function(){
img.onload = null;
2018-05-05 16:59:53 +08:00
typeof callback === 'function' && callback(img);
2017-08-21 08:51:13 +08:00
};
img.onerror = function(e){
img.onerror = null;
2018-05-05 16:59:53 +08:00
typeof error === 'function' && error(e);
2023-11-14 22:14:19 +08:00
};
2017-08-21 08:51:13 +08:00
};
/**
* location.hash 路由解析
* @param {string} hash
* @returns {Object}
*/
Class.prototype.router = Class.prototype.hash = function(hash) {
2023-05-04 10:17:04 +08:00
var that = this;
var hash = hash || location.hash;
var data = {
path: [],
search: {},
2025-01-20 10:25:36 +08:00
hash: (hash.match(/[^#](#.*$)/) || [])[1] || '',
href: ''
2017-08-21 08:51:13 +08:00
};
2023-11-14 22:14:19 +08:00
2025-01-20 10:25:36 +08:00
if (!/^#/.test(hash)) return data; // 禁止非路由规范
2023-05-04 10:17:04 +08:00
2025-01-20 10:25:36 +08:00
hash = hash.replace(/^#/, '');
data.href = hash;
2018-01-03 09:55:45 +08:00
hash = hash.replace(/([^#])(#.*$)/, '$1').split('/') || [];
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
// 提取 Hash 结构
that.each(hash, function(index, item) {
2017-08-21 08:51:13 +08:00
/^\w+=/.test(item) ? function(){
item = item.split('=');
data.search[item[0]] = item[1];
}() : data.path.push(item);
});
2023-11-14 22:14:19 +08:00
2017-08-21 08:51:13 +08:00
return data;
};
2023-11-14 22:14:19 +08:00
/**
* URL 解析
* @param {string} href - url 路径
* @returns {Object}
*/
Class.prototype.url = function(href) {
2023-05-04 10:17:04 +08:00
var that = this;
var data = {
2023-04-30 01:10:55 +08:00
// 提取 url 路径
pathname: function() {
2020-01-15 06:30:00 +08:00
var pathname = href
? function(){
2020-11-26 22:12:46 +08:00
var str = (href.match(/\.[^.]+?\/.+/) || [])[0] || '';
return str.replace(/^[^\/]+/, '').replace(/\?.+/, '');
2020-01-15 06:30:00 +08:00
}()
: location.pathname;
return pathname.replace(/^\//, '').split('/');
2023-05-04 10:17:04 +08:00
}(),
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
// 提取 url 参数
2023-05-04 10:17:04 +08:00
search: function(){
var obj = {};
2023-11-14 22:14:19 +08:00
var search = (href
2020-11-26 22:12:46 +08:00
? function(){
var str = (href.match(/\?.+/) || [])[0] || '';
return str.replace(/\#.+/, '');
}()
2020-01-15 06:30:00 +08:00
: location.search
2023-04-30 01:10:55 +08:00
).replace(/^\?+/, '').split('&'); // 去除 ?,按 & 分割参数
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
// 遍历分割后的参数
that.each(search, function(index, item) {
var _index = item.indexOf('=');
var key = function() { // 提取 key
if (_index < 0) {
2020-01-15 06:30:00 +08:00
return item.substr(0, item.length);
} else if(_index === 0) {
2020-01-15 06:30:00 +08:00
return false;
} else {
return item.substr(0, _index);
}
2023-11-14 22:14:19 +08:00
}();
2023-04-30 01:10:55 +08:00
// 提取 value
if (key) {
2020-01-15 06:30:00 +08:00
obj[key] = _index > 0 ? item.substr(_index + 1) : null;
}
});
2023-11-14 22:14:19 +08:00
2020-01-15 06:30:00 +08:00
return obj;
2023-05-04 10:17:04 +08:00
}(),
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
// 提取 Hash
hash: that.router(function() {
2023-11-14 22:14:19 +08:00
return href
2021-05-08 06:31:19 +08:00
? ((href.match(/#.+/) || [])[0] || '/')
2020-01-15 06:30:00 +08:00
: location.hash;
}())
};
2023-11-14 22:14:19 +08:00
2020-01-15 06:30:00 +08:00
return data;
};
2017-08-21 08:51:13 +08:00
/**
* 本地持久存储
* @param {string} table - 表名
* @param {Object} settings - 设置项
* @param {Storage} storage - 存储对象localStorage sessionStorage
* @returns {Object}
*/
Class.prototype.data = function(table, settings, storage) {
2017-08-21 08:51:13 +08:00
table = table || 'layui';
2018-01-03 09:55:45 +08:00
storage = storage || localStorage;
2023-11-14 22:14:19 +08:00
2022-07-03 21:32:50 +08:00
// 如果 settings 为 null则删除表
if (settings === null) {
2018-01-03 09:55:45 +08:00
return delete storage[table];
2017-08-21 08:51:13 +08:00
}
2023-11-14 22:14:19 +08:00
settings = typeof settings === 'object'
? settings
2017-08-21 08:51:13 +08:00
: {key: settings};
2023-11-14 22:14:19 +08:00
2023-05-04 10:17:04 +08:00
try {
2018-01-03 09:55:45 +08:00
var data = JSON.parse(storage[table]);
2023-05-04 10:17:04 +08:00
} catch(e) {
2017-08-21 08:51:13 +08:00
var data = {};
}
2023-11-14 22:14:19 +08:00
if ('value' in settings) data[settings.key] = settings.value;
if (settings.remove) delete data[settings.key];
2018-01-03 09:55:45 +08:00
storage[table] = JSON.stringify(data);
2023-11-14 22:14:19 +08:00
2017-08-21 08:51:13 +08:00
return settings.key ? data[settings.key] : data;
};
2023-11-14 22:14:19 +08:00
/**
* 本地临时存储
* @param {string} table - 表名
* @param {Object} settings - 设置项
* @returns {Object}
*/
Class.prototype.sessionData = function(table, settings) {
2018-01-03 09:55:45 +08:00
return this.data(table, settings, sessionStorage);
}
2017-08-21 08:51:13 +08:00
/**
* 设备信息
* @param {string} key - 任意 key
* @returns {Object}
*/
Class.prototype.device = function(key) {
2023-05-04 10:17:04 +08:00
var agent = navigator.userAgent.toLowerCase();
2017-08-21 08:51:13 +08:00
2023-04-30 01:10:55 +08:00
// 获取版本号
var getVersion = function(label) {
2017-08-21 08:51:13 +08:00
var exp = new RegExp(label + '/([^\\s\\_\\-]+)');
label = (agent.match(exp)||[])[1];
return label || false;
2023-05-04 10:17:04 +08:00
};
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
// 返回结果集
2023-05-04 10:17:04 +08:00
var result = {
os: function() { // 底层操作系统
if (/windows/.test(agent)) {
2017-08-21 08:51:13 +08:00
return 'windows';
} else if(/linux/.test(agent)) {
2017-08-21 08:51:13 +08:00
return 'linux';
} else if(/iphone|ipod|ipad|ios/.test(agent)) {
2017-08-21 08:51:13 +08:00
return 'ios';
} else if(/mac/.test(agent)) {
2017-08-21 08:51:13 +08:00
return 'mac';
2023-11-14 22:14:19 +08:00
}
2023-05-04 10:17:04 +08:00
}(),
ie: function() { // ie 版本
return (!!window.ActiveXObject || "ActiveXObject" in window) ? (
2023-04-30 01:10:55 +08:00
(agent.match(/msie\s(\d+)/) || [])[1] || '11' // 由于 ie11 并没有 msie 的标识
2017-08-21 08:51:13 +08:00
) : false;
2023-05-04 10:17:04 +08:00
}(),
weixin: getVersion('micromessenger') // 是否微信
2017-08-21 08:51:13 +08:00
};
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
// 任意的 key
if (key && !result[key]) {
2017-08-21 08:51:13 +08:00
result[key] = getVersion(key);
}
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
// 移动设备
2017-08-21 08:51:13 +08:00
result.android = /android/.test(agent);
result.ios = result.os === 'ios';
2023-06-17 23:33:54 +08:00
result.mobile = (result.android || result.ios);
2023-11-14 22:14:19 +08:00
2017-08-21 08:51:13 +08:00
return result;
};
2023-04-30 01:10:55 +08:00
// 提示
Class.prototype.hint = function() {
2017-08-21 08:51:13 +08:00
return {
error: error
2021-05-18 02:42:31 +08:00
};
};
2023-11-14 22:14:19 +08:00
/**
* typeof 类型细分 -> string/number/boolean/undefined/nullobject/array/function/
* @param {*} operand - 任意值
* @returns {string}
*/
Class.prototype._typeof = Class.prototype.type = function(operand) {
2021-05-18 02:42:31 +08:00
if(operand === null) return String(operand);
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
// 细分引用类型
return (typeof operand === 'object' || typeof operand === 'function') ? function() {
2023-05-04 10:17:04 +08:00
var type = Object.prototype.toString.call(operand).match(/\s(.+)\]$/) || []; // 匹配类型字符
var classType = 'Function|Array|Date|RegExp|Object|Error|Symbol'; // 常见类型字符
2023-11-14 22:14:19 +08:00
2021-05-18 02:42:31 +08:00
type = type[1] || 'Object';
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
// 除匹配到的类型外,其他对象均返回 object
2023-11-14 22:14:19 +08:00
return new RegExp('\\b('+ classType + ')\\b').test(type)
? type.toLowerCase()
2021-05-18 02:42:31 +08:00
: 'object';
}() : typeof operand;
};
2023-11-14 22:14:19 +08:00
/**
* 对象是否具备数组结构此处为兼容 jQuery 对象
* @param {Object} obj - 任意对象
* @returns {boolean}
*/
Class.prototype._isArray = Class.prototype.isArray = function(obj) {
2023-05-04 10:17:04 +08:00
var that = this;
var len;
var type = that.type(obj);
2023-11-14 22:14:19 +08:00
if (!obj || (typeof obj !== 'object') || obj === window) return false;
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
len = 'length' in obj && obj.length; // 兼容 ie
2021-05-18 02:42:31 +08:00
return type === 'array' || len === 0 || (
2023-04-30 01:10:55 +08:00
typeof len === 'number' && len > 0 && (len - 1) in obj // 兼容 jQuery 对象
2021-05-18 02:42:31 +08:00
);
2017-08-21 08:51:13 +08:00
};
/**
* 遍历
* @param {Object} obj - 任意对象
* @param {Function} fn - 遍历回调
*/
Class.prototype.each = function(obj, fn) {
2023-05-04 10:17:04 +08:00
var key;
var that = this;
var callback = function(key, obj) {
return fn.call(obj[key], key, obj[key]);
2021-05-08 06:31:19 +08:00
};
2023-11-14 22:14:19 +08:00
if (typeof fn !== 'function') {
return that;
}
2017-08-21 08:51:13 +08:00
obj = obj || [];
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
// 优先处理数组结构
if (that.isArray(obj)) {
for (key = 0; key < obj.length; key++) {
if(callback(key, obj)) break;
2017-08-21 08:51:13 +08:00
}
} else {
for (key in obj) {
if(callback(key, obj)) break;
2017-08-21 08:51:13 +08:00
}
}
2023-11-14 22:14:19 +08:00
2017-08-21 08:51:13 +08:00
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) {
2023-05-04 10:17:04 +08:00
var that = this;
var clone = notClone ? (arr || []) : JSON.parse(
2022-05-18 22:35:13 +08:00
JSON.stringify(arr || [])
2017-08-21 08:51:13 +08:00
);
2022-05-18 22:35:13 +08:00
// 若未传入 key则直接返回原对象
if (that.type(arr) === 'object' && !key) {
2022-05-18 22:35:13 +08:00
return clone;
} else if(typeof arr !== 'object') { // 若 arr 非对象
2022-05-18 22:35:13 +08:00
return [clone];
}
2023-11-14 22:14:19 +08:00
2022-05-18 22:35:13 +08:00
// 开始排序
clone.sort(function(o1, o2) {
2023-05-04 10:17:04 +08:00
var v1 = o1[key];
var v2 = o2[key];
2023-11-14 22:14:19 +08:00
2022-05-18 22:35:13 +08:00
/*
* 特殊数据
* 若比较的成员均非对象
*/
// 若比较的成员均为数字
if (!isNaN(o1) && !isNaN(o2)) return o1 - o2;
2022-05-18 22:35:13 +08:00
// 若比较的成员只存在某一个非对象
if (!isNaN(o1) && isNaN(o2)) {
2022-05-18 22:35:13 +08:00
if(key && typeof o2 === 'object'){
v1 = o1;
} else {
return -1;
}
} else if (isNaN(o1) && !isNaN(o2)) {
if (key && typeof o1 === 'object') {
2022-05-18 22:35:13 +08:00
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 空
2021-05-31 08:57:00 +08:00
return 1;
} else if((!v1 && v1 !== 0) && v2) { // 空 vs 数字
2021-05-31 08:57:00 +08:00
return -1;
2023-04-30 01:10:55 +08:00
} else { // 数字 vs 数字
2021-05-31 08:57:00 +08:00
return v1 - v2;
}
}
2023-11-14 22:14:19 +08:00
2021-05-31 08:57:00 +08:00
/**
* 字典序排序
*/
2023-11-14 22:14:19 +08:00
2022-05-18 22:35:13 +08:00
// 若为非数字比较
if (!isNum[0] && !isNum[1]) {
2022-05-18 22:35:13 +08:00
// 字典序比较
if (v1 > v2) {
2021-05-31 08:57:00 +08:00
return 1;
} else if (v1 < v2) {
return -1;
} else {
return 0;
}
}
2023-11-14 22:14:19 +08:00
2022-05-18 22:35:13 +08:00
// 若为混合比较
if (isNum[0] || !isNum[1]) { // 数字 vs 非数字
2021-05-31 08:57:00 +08:00
return -1;
2023-04-30 01:10:55 +08:00
} else if(!isNum[0] || isNum[1]) { // 非数字 vs 数字
2021-05-31 08:57:00 +08:00
return 1;
}
2022-05-18 22:35:13 +08:00
2017-08-21 08:51:13 +08:00
});
2017-08-30 17:10:33 +08:00
2022-05-18 22:35:13 +08:00
desc && clone.reverse(); // 倒序
2017-08-21 08:51:13 +08:00
return clone;
};
/**
* 阻止事件冒泡
* @param {Event} thisEvent - 事件对象
*/
Class.prototype.stope = function(thisEvent) {
try {
thisEvent.stopPropagation();
} catch(e) {
2018-01-03 09:55:45 +08:00
thisEvent.cancelBubble = true;
}
2017-08-21 08:51:13 +08:00
};
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
// 字符常理
2021-05-18 02:42:31 +08:00
var EV_REMOVE = 'LAYUI-EVENT-REMOVE';
2017-08-21 08:51:13 +08:00
/**
* 自定义模块事件
* @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);
2017-08-21 08:51:13 +08:00
};
/**
* 执行自定义模块事件
* @param {string} modName - 模块名
* @param {string} events - 事件名
* @param {Object} params - 参数
* @param {Function} fn - 回调
*/
Class.prototype.event = Class.event = function(modName, events, params, fn) {
2023-05-04 10:17:04 +08:00
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) {
2017-08-21 08:51:13 +08:00
var res = item && item.call(that, params);
res === false && result === null && (result = false);
};
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
// 如果参数传入特定字符,则执行移除事件
if (params === EV_REMOVE) {
2020-11-26 22:12:46 +08:00
delete (that.cache.event[eventName] || {})[filterName];
return that;
}
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
// 添加事件
if (fn) {
cache.event[eventName] = cache.event[eventName] || {};
2018-01-03 09:55:45 +08:00
if (filterName) {
// 带 filter 不支持重复事件
cache.event[eventName][filterName] = [fn];
} else {
// 不带 filter 处理的是所有的同类事件,应该支持重复事件
cache.event[eventName][filterName] = cache.event[eventName][filterName] || [];
cache.event[eventName][filterName].push(fn);
}
2018-01-03 09:55:45 +08:00
return this;
}
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
// 执行事件回调
layui.each(cache.event[eventName], function(key, item) {
2023-04-30 01:10:55 +08:00
// 执行当前模块的全部事件
if (filterName === '{*}') {
2018-01-03 09:55:45 +08:00
layui.each(item, callback);
return;
}
2023-11-14 22:14:19 +08:00
2023-04-30 01:10:55 +08:00
// 执行指定事件
2018-01-03 09:55:45 +08:00
key === '' && layui.each(item, callback);
2018-11-01 02:50:21 +08:00
(filterName && key === filterName) && layui.each(item, callback);
2018-01-03 09:55:45 +08:00
});
2023-11-14 22:14:19 +08:00
2017-08-21 08:51:13 +08:00
return result;
};
2023-11-14 22:14:19 +08:00
/**
* 新增模块事件
* @param {string} events - 事件名
* @param {string} modName - 模块名
* @param {Function} callback - 回调
* @returns {Object}
*/
Class.prototype.on = function(events, modName, callback) {
2020-11-26 22:12:46 +08:00
var that = this;
return that.onevent.call(that, modName, events, callback);
}
2023-11-14 22:14:19 +08:00
/**
* 移除模块事件
* @param {string} events - 事件名
* @param {string} modName - 模块名
* @returns {Object}
*/
Class.prototype.off = function(events, modName) {
2020-11-26 22:12:46 +08:00
var that = this;
2021-05-18 02:42:31 +08:00
return that.event.call(that, modName, events, EV_REMOVE);
2020-11-26 22:12:46 +08:00
};
2023-05-06 17:42:38 +08:00
/**
* 防抖
* @param {Function} func - 回调
* @param {number} wait - 延时执行的毫秒数
* @returns {Function}
*/
Class.prototype.debounce = function (func, wait) {
2023-05-06 17:42:38 +08:00
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) {
2023-05-06 17:42:38 +08:00
var cooldown = false;
return function () {
var context = this;
var args = arguments;
if (!cooldown) {
func.apply(context, args);
cooldown = true;
setTimeout(function () {
cooldown = false;
}, wait);
}
}
};
// export layui
window.layui = new Class();
})(window);