mirror of
https://gitee.com/layui/layui.git
synced 2025-06-28 04:35:24 +08:00
1025 lines
28 KiB
JavaScript
1025 lines
28 KiB
JavaScript
/**
|
||
* Layui
|
||
* Classic modular front-end UI library
|
||
* MIT Licensed
|
||
*/
|
||
|
||
(function(window) {
|
||
'use strict';
|
||
|
||
// 便于打包时的字符压缩
|
||
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 = '2.11.0-rc.3'; // 版本号
|
||
};
|
||
|
||
// 识别预先可能定义的指定全局对象
|
||
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 || 'log';
|
||
window.console && console[type] && console[type]('layui error hint: ' + msg);
|
||
};
|
||
|
||
// 内置模块
|
||
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', // 标签页
|
||
element: 'element', // 常用元素操作
|
||
rate: 'rate', // 评分组件
|
||
colorpicker: 'colorpicker', // 颜色选择器
|
||
slider: 'slider', // 滑块
|
||
carousel: 'carousel', // 轮播
|
||
flow: 'flow', // 流加载
|
||
util: 'util', // 工具块
|
||
code: 'code', // 代码修饰器
|
||
jquery: 'jquery', // DOM 库(第三方)
|
||
component: 'component', // 组件构建器
|
||
|
||
all: 'all',
|
||
'layui.all': 'layui.all' // 聚合标识(功能性的,非真实模块)
|
||
};
|
||
|
||
/**
|
||
* 低版本浏览器适配
|
||
* @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;
|
||
};
|
||
|
||
/**
|
||
* 定义模块
|
||
* @param {(string|string[])} deps - 依赖的模块列表
|
||
* @param {Function} callback - 模块的回调
|
||
*/
|
||
Class.prototype.define = function(deps, callback) {
|
||
var that = this;
|
||
var useCallback = function() {
|
||
var setModule = function(mod, exports) {
|
||
layui[mod] = exports; // 将模块接口赋值在 layui 对象中
|
||
cache.status[mod] = true; // 标记模块注册完成
|
||
};
|
||
// 执行模块的回调
|
||
typeof callback === 'function' && callback(function(mod, exports) {
|
||
setModule(mod, exports);
|
||
// 记录模块回调,以便需要时再执行
|
||
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, 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 && jQuery.fn.on) {
|
||
that.each(mods, function(index, item) {
|
||
if (item === 'jquery') {
|
||
mods.splice(index, 1);
|
||
}
|
||
});
|
||
layui.jquery = layui.$ = window.jQuery;
|
||
}
|
||
|
||
// 将模块的接口作为回调的参数传递
|
||
exports = exports || [];
|
||
|
||
// 加载当前队列的第一个模块
|
||
var item = mods[0];
|
||
var modInfo = that.modules[item]; // 当前模块信息
|
||
// 是否为外部模块,即无需遵循 layui 轻量级模块规范的任意第三方组件。
|
||
var isExternalModule = typeof modInfo === 'object';
|
||
|
||
// 回调触发
|
||
var onCallback = function () {
|
||
exports.push(layui[item]);
|
||
mods.length > 1
|
||
? that.use(mods.slice(1), callback, exports, from)
|
||
: (typeof callback === 'function' && function() {
|
||
// 保证文档加载完毕再执行回调
|
||
if(layui.jquery && typeof layui.jquery === 'function' && from !== 'define'){
|
||
return layui.jquery(function(){
|
||
callback.apply(layui, exports);
|
||
});
|
||
}
|
||
callback.apply(layui, exports);
|
||
}() );
|
||
};
|
||
|
||
// 回调轮询
|
||
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 link = document.createElement('link');
|
||
|
||
// 若第二个参数为 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;
|
||
|
||
link.href = href + (config.debug ? '?v='+new Date().getTime() : '');
|
||
link.rel = 'stylesheet';
|
||
link.id = id;
|
||
|
||
// 插入节点
|
||
if (!document.getElementById(id)) {
|
||
head.appendChild(link);
|
||
}
|
||
|
||
// 是否执行回调
|
||
if (typeof callback !== 'function') {
|
||
return that;
|
||
}
|
||
|
||
onNodeLoad(link, function() {
|
||
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) {
|
||
var that = this;
|
||
var hash = hash || location.hash;
|
||
var data = {
|
||
path: [],
|
||
search: {},
|
||
hash: (hash.match(/[^#](#.*$)/) || [])[1] || '',
|
||
href: ''
|
||
};
|
||
|
||
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);
|
||
});
|
||
|
||
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};
|
||
|
||
try {
|
||
var data = JSON.parse(storage[table]);
|
||
} catch(e) {
|
||
var 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
|
||
};
|
||
};
|
||
|
||
/**
|
||
* 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(e) {
|
||
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);
|
||
}
|
||
}
|
||
};
|
||
|
||
// export layui
|
||
window.layui = new Class();
|
||
})(window);
|