diff --git a/examples/extend.html b/examples/extend.html
index 6669b84f..d265a12a 100644
--- a/examples/extend.html
+++ b/examples/extend.html
@@ -1,30 +1,56 @@
-
-
-
-自定义模块 - layui
+
+
+
+ 扩展模块 - Layui
+
+
+
+
+
打开浏览器控制台查看测试结果
+
+
+
-
-
+ // 加载外部样式
+ layui.link('https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css', function(link) {
+ console.log('prism.min.css loaded');
+ }, 'prism');
+
+
diff --git a/examples/js/index.js b/examples/extends/index.js
similarity index 62%
rename from examples/js/index.js
rename to examples/extends/index.js
index e4eca313..e6fbbd77 100644
--- a/examples/js/index.js
+++ b/examples/extends/index.js
@@ -1,8 +1,8 @@
layui.define(function(exports){
-
+
exports('index', {
- title: '模块入口'
+ title: 'index 扩展模块'
});
-});
\ No newline at end of file
+});
diff --git a/examples/extends/temp.js b/examples/extends/temp.js
deleted file mode 100644
index d7487d59..00000000
--- a/examples/extends/temp.js
+++ /dev/null
@@ -1,143 +0,0 @@
-/**
-
- @Name:layui.modDemo XX组件
- @Author:贤心
- @License:MIT
-
- */
-
-layui.define(['laytpl'], function(exports){
- "use strict";
-
- var $ = layui.$
- ,laytpl = layui.laytpl
-
- //模块名
- ,MOD_NAME = 'modDemo'
-
- //外部接口
- ,modeDemo = {
- config: {}
- ,index: layui[MOD_NAME] ? (layui[MOD_NAME].index + 10000) : 0
-
- //设置全局项
- ,set: function(options){
- var that = this;
- that.config = $.extend({}, that.config, options);
- return that;
- }
-
- //事件监听
- ,on: function(events, callback){
- return layui.onevent.call(this, MOD_NAME, events, callback);
- }
- }
-
- //操作当前实例
- ,thisModule = function(){
- var that = this
- ,options = that.config
- ,id = options.id || that.index;
-
- thisModule.that[id] = that; //记录当前实例对象
- thisModule.config[id] = options; //记录当前实例配置项
-
- return {
- config: options
- //重置实例
- ,reload: function(options){
- that.reload.call(that, options);
- }
- }
- }
-
- //获取当前实例配置项
- ,getThisModuleConfig = function(id){
- var config = thisModule.config[id];
- if(!config) hint.error('The ID option was not found in the '+ MOD_NAME +' instance');
- return config || null;
- }
-
- //字符常量
- ,ELEM = 'layui-modeDemo'
-
-
- //主模板
- ,TPL_MAIN = [''
-
- ,'
'].join('')
-
- //构造器
- ,Class = function(options){
- var that = this;
- that.index = ++transfer.index;
- that.config = $.extend({}, that.config, transfer.config, options);
- that.render();
- };
-
- //默认配置
- Class.prototype.config = {
-
- };
-
- //重载实例
- Class.prototype.reload = function(options){
- var that = this;
-
- layui.each(options, function(key, item){
- if(item.constructor === Array) delete that.config[key];
- });
-
- that.config = $.extend(true, {}, that.config, options);
- that.render();
- };
-
- //渲染
- Class.prototype.render = function(){
- var that = this
- ,options = that.config;
-
- //解析模板
- that.elem = $(TPL_MAIN);
-
- var othis = options.elem = $(options.elem);
- if(!othis[0]) return;
-
- //索引
- that.key = options.id || that.index;
-
- //插入组件结构
- othis.html(that.elem);
-
- that.events(); //事件
- };
-
- //事件
- Class.prototype.events = function(){
- var that = this;
-
-
- };
-
-
-
- //记录所有实例
- thisModule.that = {}; //记录所有实例对象
- thisModule.config = {}; //记录所有实例配置项
-
- //重载实例
- modeDemo.reload = function(id, options){
- var that = thisModule.that[id];
- that.reload(options);
-
- return thisModule.call(that);
- };
-
- //核心入口
- modeDemo.render = function(options){
- var inst = new Class(options);
- return thisTransfer.call(inst);
- };
-
- exports(MOD_NAME, modeDemo);
-});
diff --git a/examples/js/child/test.js b/examples/extends/test.js
similarity index 53%
rename from examples/js/child/test.js
rename to examples/extends/test.js
index 56083520..94fc194b 100644
--- a/examples/js/child/test.js
+++ b/examples/extends/test.js
@@ -1,8 +1,9 @@
-
+/**
+ * test
+ */
layui.define(function(exports){
-
exports('test', {
- title: '子目录模块加载'
+ title: 'test 扩展模块'
})
-});
\ No newline at end of file
+});
diff --git a/examples/extends/test/test1.js b/examples/extends/test/test1.js
new file mode 100644
index 00000000..f66f7d26
--- /dev/null
+++ b/examples/extends/test/test1.js
@@ -0,0 +1,9 @@
+/**
+ * test1
+ */
+
+layui.define(function(exports){
+ exports('test1', {
+ title: 'test1 扩展模块'
+ })
+});
diff --git a/examples/extends/test/test2.js b/examples/extends/test/test2.js
new file mode 100644
index 00000000..5a15c077
--- /dev/null
+++ b/examples/extends/test/test2.js
@@ -0,0 +1,9 @@
+/**
+ * test2
+ */
+
+layui.define(function(exports){
+ exports('test2', {
+ title: 'test2 扩展模块'
+ })
+});
diff --git a/src/layui.js b/src/layui.js
index 78928f05..1867ec03 100644
--- a/src/layui.js
+++ b/src/layui.js
@@ -4,28 +4,40 @@
* MIT Licensed
*/
-;!function(win){
- "use strict";
+(function(window) {
+ 'use strict';
- var doc = win.document;
+ // 便于打包时的字符压缩
+ var document = window.document;
+ var location = window.location;
+
+ // 基础配置
var config = {
- modules: {}, // 模块物理路径
- status: {}, // 模块加载状态
timeout: 10, // 符合规范的模块请求最长等待秒数
- event: {} // 模块自定义事件
+ debug: false, // 是否开启调试模式
+ version: false // 是否在模块请求时加入版本号参数(以更新模块缓存)
};
- var Layui = function(){
- this.v = '2.10.0'; // Layui 版本号
+ // 模块加载缓存信息
+ var cache = {
+ modules: {}, // 模块物理路径
+ status: {}, // 模块加载就绪状态
+ event: {}, // 模块自定义事件
+ callback: {} // 模块的回调
+ };
+
+ // constructor
+ var Class = function() {
+ this.v = '2.11.0-beta.1'; // 版本号
};
// 识别预先可能定义的指定全局对象
- var GLOBAL = win.LAYUI_GLOBAL || {};
+ var GLOBAL = window.LAYUI_GLOBAL || {};
// 获取 layui 所在目录
- var getPath = function(){
- var jsPath = (doc.currentScript && doc.currentScript.tagName.toUpperCase() === 'SCRIPT') ? doc.currentScript.src : function(){
- var js = doc.getElementsByTagName('script');
+ 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--){
@@ -41,15 +53,13 @@
}();
// 异常提示
- var error = function(msg, type){
+ var error = function(msg, type) {
type = type || 'log';
- win.console && console[type] && console[type]('layui error hint: ' + msg);
+ window.console && console[type] && console[type]('layui error hint: ' + msg);
};
- var isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]';
-
// 内置模块
- var modules = config.builtin = {
+ var builtinModules = config.builtin = {
lay: 'lay', // 基础 DOM 操作
layer: 'layer', // 弹层
laydate: 'laydate', // 日期
@@ -78,93 +88,174 @@
'layui.all': 'layui.all' // 聚合标识(功能性的,非真实模块)
};
- // 记录基础数据
- Layui.prototype.cache = config;
+ /**
+ * 低版本浏览器适配
+ * @see polyfill
+ */
- // 定义模块
- Layui.prototype.define = function(deps, factory){
+ // 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 type = typeof deps === 'function';
- var callback = function(){
- var setApp = function(app, exports){
- layui[app] = exports;
- config.status[app] = true;
+ var useCallback = function() {
+ var setModule = function(mod, exports) {
+ layui[mod] = exports; // 将模块接口赋值在 layui 对象中
+ cache.status[mod] = true; // 标记模块注册完成
};
- typeof factory === 'function' && factory(function(app, exports){
- setApp(app, exports);
- config.callback[app] = function(){
- factory(setApp);
+ // 执行模块的回调
+ typeof callback === 'function' && callback(function(mod, exports) {
+ setModule(mod, exports);
+ // 记录模块回调,以便需要时再执行
+ cache.callback[mod] = function() {
+ callback(setModule);
}
});
return this;
};
- type && (
- factory = deps,
- deps = []
- );
+ // 若未依赖模块
+ if (typeof deps === 'function') {
+ callback = deps;
+ deps = [];
+ }
- that.use(deps, callback, null, 'define');
+ that.use(deps, useCallback, null, 'define');
return that;
};
- // 使用特定模块
- Layui.prototype.use = function(apps, callback, exports, from){
+ /**
+ * 使用模块
+ * @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;
- var head = doc.getElementsByTagName('head')[0];
- apps = function(){
- if(typeof apps === 'string'){
- return [apps];
+ // 整理模块队列
+ mods = (function() {
+ if (typeof mods === 'string') {
+ return [mods];
}
- // 当第一个参数为 function 时,则自动加载所有内置模块,且执行的回调即为该 function 参数;
- else if(typeof apps === 'function'){
- callback = apps;
+ // 若第一个参数为 function ,则自动加载所有内置模块,且执行的回调即为该 function 参数;
+ else if(typeof mods === 'function') {
+ callback = mods;
return ['all'];
}
- return apps;
- }();
+ return mods;
+ })();
- // 如果页面已经存在 jQuery 1.7+ 库且所定义的模块依赖 jQuery,则不加载内部 jquery 模块
- if(win.jQuery && jQuery.fn.on){
- that.each(apps, function(index, item){
- if(item === 'jquery'){
- apps.splice(index, 1);
+ // 获取 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.$ = jQuery;
+ layui.jquery = layui.$ = window.jQuery;
}
- var item = apps[0];
- var timeout = 0;
-
+ // 将模块的接口作为回调的参数传递
exports = exports || [];
- // 静态资源host
- config.host = config.host || (dir.match(/\/\/([\s\S]+?)\//)||['//'+ location.host +'/'])[0];
+ // 加载当前队列的第一个模块
+ var item = mods[0];
+ var modInfo = that.modules[item]; // 当前模块信息
+ // 是否为外部模块,即无需遵循 layui 轻量级模块规范的任意第三方组件。
+ var isExternalModule = typeof modInfo === 'object';
- // 加载完毕
- function onScriptLoad(e, url){
- var readyRegExp = navigator.platform === 'PLaySTATION 3' ? /^complete$/ : /^(complete|loaded)$/
- if (e.type === 'load' || (readyRegExp.test((e.currentTarget || e.srcElement).readyState))) {
- config.modules[item] = url;
- head.removeChild(node);
- (function poll() {
- if(++timeout > config.timeout * 1000 / 4){
- return error(item + ' is not a valid module', 'error');
- }
- config.status[item] ? onCallback() : setTimeout(poll, 4);
- }());
- }
- }
-
- // 回调
- function onCallback(){
+ // 回调触发
+ var onCallback = function () {
exports.push(layui[item]);
- apps.length > 1 ?
- that.use(apps.slice(1), callback, exports, from)
- : ( typeof callback === 'function' && function(){
+ 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(){
@@ -173,182 +264,241 @@
}
callback.apply(layui, exports);
}() );
- }
+ };
- // 如果引入了聚合板,内置的模块则不必重复加载
- if( apps.length === 0 || (layui['layui.all'] && modules[item]) ){
+ // 回调轮询
+ 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;
}
- /*
- * 获取加载的模块 URL
- * 如果是内置模块,则按照 dir 参数拼接模块路径
- * 如果是扩展模块,则判断模块路径值是否为 {/} 开头,
- * 如果路径值是 {/} 开头,则模块路径即为后面紧跟的字符。
- * 否则,则按照 base 参数拼接模块路径
- */
+ // 当前模块所在路径
+ var modSrc = isExternalModule ? modInfo.src : modInfo;
- var url = ( modules[item] ? (dir + 'modules/')
- : (/^\{\/\}/.test(that.modules[item]) ? '' : (config.base || ''))
- ) + (that.modules[item] || item) + '.js';
- url = url.replace(/^\{\/\}/, '');
+ // 基础路径
+ var basePath = builtinModules[item]
+ ? (dir + 'modules/') // 若为内置模块,则按照默认 dir 参数拼接模块 URL
+ : (modSrc ? '' : config.base); // 若为扩展模块,且模块路径已设置,则不必再重复拼接基础路径
- // 如果扩展模块(即:非内置模块)对象已经存在,则不必再加载
- if(!config.modules[item] && layui[item]){
- config.modules[item] = url; // 并记录起该扩展模块的 url
+ // 若从 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(!config.modules[item]){
- var node = doc.createElement('script');
+ if (!cache.modules[item]) {
+ var head = document.getElementsByTagName('head')[0];
+ var node = document.createElement('script');
node.async = true;
- node.charset = 'utf-8';
- node.src = url + function(){
+ node.charset = 'utf-8'; // 避免 IE9 的编码问题
+ node.src = url + function() {
var version = config.version === true
- ? (config.v || (new Date()).getTime())
- : (config.version||'');
+ ? (config.v || (new Date()).getTime())
+ : (config.version || '');
return version ? ('?v=' + version) : '';
}();
head.appendChild(node);
- if(node.attachEvent && !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && !isOpera){
- node.attachEvent('onreadystatechange', function(e){
- onScriptLoad(e, url);
- });
- } else {
- node.addEventListener('load', function(e){
- onScriptLoad(e, url);
- }, false);
- }
+ // 节点加载事件
+ onNodeLoad(node, function() {
+ head.removeChild(node);
+ pollCallback();
+ }, function() {
+ head.removeChild(node);
+ });
- config.modules[item] = url;
- } else { // 缓存
- (function poll() {
- if(++timeout > config.timeout * 1000 / 4){
- return error(item + ' is not a valid module', 'error');
- }
- (typeof config.modules[item] === 'string' && config.status[item])
- ? onCallback()
- : setTimeout(poll, 4);
- }());
+ // 模块已首次加载的标记
+ cache.modules[item] = url;
+ } else { // 再次 use 模块
+ pollCallback();
}
return that;
};
- // 弃用原有的指定模块,以便重新扩展新的同名模块
- Layui.prototype.disuse = function(apps){
+ // 记录全部模块
+ Class.prototype.modules = Object.assign({}, builtinModules);
+
+ /**
+ * 拓展模块
+ * @param {Object} settings - 拓展模块的配置
+ */
+ Class.prototype.extend = function(settings) {
var that = this;
- apps = that.isArray(apps) ? apps : [apps];
- that.each(apps, function (index, item) {
- if (!config.status[item]) {
- // return error('module ' + item + ' is not exist');
+ 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 modules[item];
+ delete builtinModules[item];
delete that.modules[item];
- delete config.status[item];
- delete config.modules[item];
+ delete cache.status[item];
+ delete cache.modules[item];
});
return that;
};
- // 获取节点的 style 属性值
- // currentStyle.getAttribute 参数为 camelCase 形式的字符串
- Layui.prototype.getStyle = function(node, name){
- var style = node.currentStyle ? node.currentStyle : win.getComputedStyle(node, null);
+ /**
+ * 获取节点的 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 外部加载器
- Layui.prototype.link = function(href, fn, cssname){
+ /**
+ * 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 = doc.getElementsByTagName('head')[0];
- var link = doc.createElement('link');
+ var head = document.getElementsByTagName('head')[0];
+ var link = document.createElement('link');
- if(typeof fn === 'string') cssname = fn;
+ // 若第二个参数为 string 类型,则该参数为 id
+ if (typeof callback === 'string') {
+ id = callback;
+ }
- var app = (cssname || href).replace(/\.|\//g, '');
- var id = 'layuicss-'+ app;
- var STAUTS_NAME = 'creating';
- var timeout = 0;
+ // 若加载多个
+ 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;
- link.media = 'all';
- if(!doc.getElementById(id)){
+ // 插入节点
+ if (!document.getElementById(id)) {
head.appendChild(link);
}
- if(typeof fn !== 'function') return that;
+ // 是否执行回调
+ if (typeof callback !== 'function') {
+ return that;
+ }
- // 轮询 css 是否加载完毕
- (function poll(status) {
- var delay = 100;
- var getLinkElem = doc.getElementById(id); // 获取动态插入的 link 元素
-
- // 如果轮询超过指定秒数,则视为请求文件失败或 css 文件不符合规范
- if(++timeout > config.timeout * 1000 / delay){
- return error(href + ' timeout');
- }
-
- // css 加载就绪
- if(parseInt(that.getStyle(getLinkElem, 'width')) === 1989){
- // 如果参数来自于初始轮询(即未加载就绪时的),则移除 link 标签状态
- if(status === STAUTS_NAME) getLinkElem.removeAttribute('lay-status');
- // 如果 link 标签的状态仍为「创建中」,则继续进入轮询,直到状态改变,则执行回调
- getLinkElem.getAttribute('lay-status') === STAUTS_NAME ? setTimeout(poll, delay) : fn();
- } else {
- getLinkElem.setAttribute('lay-status', STAUTS_NAME);
- setTimeout(function(){
- poll(STAUTS_NAME);
- }, delay);
- }
- }());
-
- // 轮询css是否加载完毕
- /*
- (function poll() {
- if(++timeout > config.timeout * 1000 / 100){
- return error(href + ' timeout');
- };
- parseInt(that.getStyle(doc.getElementById(id), 'width')) === 1989 ? function(){
- fn();
- }() : setTimeout(poll, 100);
- }());
- */
+ onNodeLoad(link, function() {
+ callback(link);
+ }, function() {
+ error(href + ' load error', 'error');
+ head.removeChild(link); // 移除节点
+ });
return that;
};
- // css 内部加载器
- Layui.prototype.addcss = function(firename, fn, cssname){
- return layui.link(config.dir + 'css/' + firename, fn, cssname);
+ /**
+ * CSS 内部加载器
+ * @param {string} modName - 模块名
+ */
+ Class.prototype.addcss = function(modName, callback, id) {
+ return layui.link(config.dir + 'css/' + modName, callback, id);
};
- // 存储模块的回调
- config.callback = {};
-
- // 重新执行模块的工厂函数
- Layui.prototype.factory = function(modName){
- if(layui[modName]){
+ /**
+ * 获取执行定义模块时的回调函数,factory 为向下兼容
+ * @param {string} modName - 模块名
+ * @returns {Function}
+ */
+ Class.prototype.factory = function(modName) {
+ if (layui[modName]) {
return typeof config.callback[modName] === 'function'
? config.callback[modName]
: null;
}
};
- // 图片预加载
- Layui.prototype.img = function(url, callback, error) {
+ /**
+ * 图片预加载
+ * @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){
+ if (img.complete) {
return callback(img);
}
img.onload = function(){
@@ -361,43 +511,12 @@
};
};
- // 全局配置
- Layui.prototype.config = function(options){
- options = options || {};
- for(var key in options){
- config[key] = options[key];
- }
- return this;
- };
-
- // 记录全部模块
- Layui.prototype.modules = function(){
- var clone = {};
- for(var o in modules){
- clone[o] = modules[o];
- }
- return clone;
- }();
-
- // 拓展模块
- Layui.prototype.extend = function(options){
- var that = this;
-
- // 验证模块是否被占用
- options = options || {};
- for(var o in options){
- if(that[o] || that.modules[o]){
- error(o+ ' Module already exists', 'error');
- } else {
- that.modules[o] = options[o];
- }
- }
-
- return that;
- };
-
- // location.hash 路由解析
- Layui.prototype.router = Layui.prototype.hash = function(hash){
+ /**
+ * 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 = {
@@ -414,7 +533,7 @@
hash = hash.replace(/([^#])(#.*$)/, '$1').split('/') || [];
// 提取 Hash 结构
- that.each(hash, function(index, item){
+ that.each(hash, function(index, item) {
/^\w+=/.test(item) ? function(){
item = item.split('=');
data.search[item[0]] = item[1];
@@ -424,12 +543,16 @@
return data;
};
- // URL 解析
- Layui.prototype.url = function(href){
+ /**
+ * URL 解析
+ * @param {string} href - url 路径
+ * @returns {Object}
+ */
+ Class.prototype.url = function(href) {
var that = this;
var data = {
// 提取 url 路径
- pathname: function(){
+ pathname: function() {
var pathname = href
? function(){
var str = (href.match(/\.[^.]+?\/.+/) || [])[0] || '';
@@ -451,19 +574,19 @@
).replace(/^\?+/, '').split('&'); // 去除 ?,按 & 分割参数
// 遍历分割后的参数
- that.each(search, function(index, item){
- var _index = item.indexOf('=')
- ,key = function(){ // 提取 key
- if(_index < 0){
+ 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){
+ } else if(_index === 0) {
return false;
} else {
return item.substr(0, _index);
}
}();
// 提取 value
- if(key){
+ if (key) {
obj[key] = _index > 0 ? item.substr(_index + 1) : null;
}
});
@@ -472,7 +595,7 @@
}(),
// 提取 Hash
- hash: that.router(function(){
+ hash: that.router(function() {
return href
? ((href.match(/#.+/) || [])[0] || '/')
: location.hash;
@@ -482,15 +605,19 @@
return data;
};
- // 本地持久存储
- Layui.prototype.data = function(table, settings, storage){
+ /**
+ * 本地持久存储
+ * @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;
- if(!win.JSON || !win.JSON.parse) return;
-
// 如果 settings 为 null,则删除表
- if(settings === null){
+ if (settings === null) {
return delete storage[table];
}
@@ -504,24 +631,33 @@
var data = {};
}
- if('value' in settings) data[settings.key] = settings.value;
- if(settings.remove) delete data[settings.key];
+ 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;
};
- // 本地临时存储
- Layui.prototype.sessionData = function(table, settings){
+ /**
+ * 本地临时存储
+ * @param {string} table - 表名
+ * @param {Object} settings - 设置项
+ * @returns {Object}
+ */
+ Class.prototype.sessionData = function(table, settings) {
return this.data(table, settings, sessionStorage);
}
- // 设备信息
- Layui.prototype.device = function(key){
+ /**
+ * 设备信息
+ * @param {string} key - 任意 key
+ * @returns {Object}
+ */
+ Class.prototype.device = function(key) {
var agent = navigator.userAgent.toLowerCase();
// 获取版本号
- var getVersion = function(label){
+ var getVersion = function(label) {
var exp = new RegExp(label + '/([^\\s\\_\\-]+)');
label = (agent.match(exp)||[])[1];
return label || false;
@@ -529,19 +665,19 @@
// 返回结果集
var result = {
- os: function(){ // 底层操作系统
- if(/windows/.test(agent)){
+ os: function() { // 底层操作系统
+ if (/windows/.test(agent)) {
return 'windows';
- } else if(/linux/.test(agent)){
+ } else if(/linux/.test(agent)) {
return 'linux';
- } else if(/iphone|ipod|ipad|ios/.test(agent)){
+ } else if(/iphone|ipod|ipad|ios/.test(agent)) {
return 'ios';
- } else if(/mac/.test(agent)){
+ } else if(/mac/.test(agent)) {
return 'mac';
}
}(),
- ie: function(){ // ie 版本
- return (!!win.ActiveXObject || "ActiveXObject" in win) ? (
+ ie: function() { // ie 版本
+ return (!!window.ActiveXObject || "ActiveXObject" in window) ? (
(agent.match(/msie\s(\d+)/) || [])[1] || '11' // 由于 ie11 并没有 msie 的标识
) : false;
}(),
@@ -549,7 +685,7 @@
};
// 任意的 key
- if(key && !result[key]){
+ if (key && !result[key]) {
result[key] = getVersion(key);
}
@@ -562,18 +698,22 @@
};
// 提示
- Layui.prototype.hint = function(){
+ Class.prototype.hint = function() {
return {
error: error
};
};
- // typeof 类型细分 -> string/number/boolean/undefined/null、object/array/function/…
- Layui.prototype._typeof = Layui.prototype.type = function(operand){
+ /**
+ * 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(){
+ 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'; // 常见类型字符
@@ -586,13 +726,17 @@
}() : typeof operand;
};
- // 对象是否具备数组结构(此处为兼容 jQuery 对象)
- Layui.prototype._isArray = Layui.prototype.isArray = function(obj){
+ /**
+ * 对象是否具备数组结构(此处为兼容 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 === win) return false;
+ if (!obj || (typeof obj !== 'object') || obj === window) return false;
len = 'length' in obj && obj.length; // 兼容 ie
return type === 'array' || len === 0 || (
@@ -600,47 +744,61 @@
);
};
- // 遍历
- Layui.prototype.each = function(obj, fn){
+ /**
+ * 遍历
+ * @param {Object} obj - 任意对象
+ * @param {Function} fn - 遍历回调
+ */
+ Class.prototype.each = function(obj, fn) {
var key;
var that = this;
- var callFn = function(key, obj){ // 回调
- return fn.call(obj[key], key, obj[key])
+ var callback = function(key, obj) {
+ return fn.call(obj[key], key, obj[key]);
};
- if(typeof fn !== 'function') return that;
+ if (typeof fn !== 'function') {
+ return that;
+ }
+
obj = obj || [];
// 优先处理数组结构
- if(that.isArray(obj)){
- for(key = 0; key < obj.length; key++){
- if(callFn(key, obj)) break;
+ if (that.isArray(obj)) {
+ for (key = 0; key < obj.length; key++) {
+ if(callback(key, obj)) break;
}
} else {
- for(key in obj){
- if(callFn(key, obj)) break;
+ for (key in obj) {
+ if(callback(key, obj)) break;
}
}
return that;
};
- // 将数组中的成员对象按照某个 key 的 value 值进行排序
- Layui.prototype.sort = function(arr, key, desc, notClone){
+ /**
+ * 将数组中的成员对象按照某个 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){
+ if (that.type(arr) === 'object' && !key) {
return clone;
- } else if(typeof arr !== 'object'){ // 若 arr 非对象
+ } else if(typeof arr !== 'object') { // 若 arr 非对象
return [clone];
}
// 开始排序
- clone.sort(function(o1, o2){
+ clone.sort(function(o1, o2) {
var v1 = o1[key];
var v2 = o2[key];
@@ -650,16 +808,17 @@
*/
// 若比较的成员均为数字
- if(!isNaN(o1) && !isNaN(o2)) return o1 - o2;
+ if (!isNaN(o1) && !isNaN(o2)) return o1 - o2;
+
// 若比较的成员只存在某一个非对象
- if(!isNaN(o1) && isNaN(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'){
+ } else if (isNaN(o1) && !isNaN(o2)) {
+ if (key && typeof o1 === 'object') {
v2 = o2;
} else {
return 1;
@@ -676,10 +835,10 @@
var isNum = [!isNaN(v1), !isNaN(v2)];
// 若为数字比较
- if(isNum[0] && isNum[1]){
- if(v1 && (!v2 && v2 !== 0)){ // 数字 vs 空
+ if (isNum[0] && isNum[1]) {
+ if(v1 && (!v2 && v2 !== 0)) { // 数字 vs 空
return 1;
- } else if((!v1 && v1 !== 0) && v2){ // 空 vs 数字
+ } else if((!v1 && v1 !== 0) && v2) { // 空 vs 数字
return -1;
} else { // 数字 vs 数字
return v1 - v2;
@@ -691,9 +850,9 @@
*/
// 若为非数字比较
- if(!isNum[0] && !isNum[1]){
+ if (!isNum[0] && !isNum[1]) {
// 字典序比较
- if(v1 > v2){
+ if (v1 > v2) {
return 1;
} else if (v1 < v2) {
return -1;
@@ -703,7 +862,7 @@
}
// 若为混合比较
- if(isNum[0] || !isNum[1]){ // 数字 vs 非数字
+ if (isNum[0] || !isNum[1]) { // 数字 vs 非数字
return -1;
} else if(!isNum[0] || isNum[1]) { // 非数字 vs 数字
return 1;
@@ -715,10 +874,14 @@
return clone;
};
- // 阻止事件冒泡
- Layui.prototype.stope = function(thisEvent){
- thisEvent = thisEvent || win.event;
- try { thisEvent.stopPropagation() } catch(e){
+ /**
+ * 阻止事件冒泡
+ * @param {Event} thisEvent - 事件对象
+ */
+ Class.prototype.stope = function(thisEvent) {
+ try {
+ thisEvent.stopPropagation();
+ } catch(e) {
thisEvent.cancelBubble = true;
}
};
@@ -726,51 +889,63 @@
// 字符常理
var EV_REMOVE = 'LAYUI-EVENT-REMOVE';
- // 自定义模块事件
- Layui.prototype.onevent = function(modName, events, callback){
- if(typeof modName !== 'string'
- || typeof callback !== 'function') return this;
-
- return Layui.event(modName, events, null, callback);
+ /**
+ * 自定义模块事件
+ * @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);
};
- // 执行自定义模块事件
- Layui.prototype.event = Layui.event = function(modName, events, params, fn){
+ /**
+ * 执行自定义模块事件
+ * @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 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){
+ if (params === EV_REMOVE) {
delete (that.cache.event[eventName] || {})[filterName];
return that;
}
// 添加事件
- if(fn){
- config.event[eventName] = config.event[eventName] || {};
+ if (fn) {
+ cache.event[eventName] = cache.event[eventName] || {};
if (filterName) {
- // 带filter不支持重复事件
- config.event[eventName][filterName] = [fn];
+ // 带 filter 不支持重复事件
+ cache.event[eventName][filterName] = [fn];
} else {
- // 不带filter处理的是所有的同类事件,应该支持重复事件
- config.event[eventName][filterName] = config.event[eventName][filterName] || [];
- config.event[eventName][filterName].push(fn);
+ // 不带 filter 处理的是所有的同类事件,应该支持重复事件
+ cache.event[eventName][filterName] = cache.event[eventName][filterName] || [];
+ cache.event[eventName][filterName].push(fn);
}
return this;
}
// 执行事件回调
- layui.each(config.event[eventName], function(key, item){
+ layui.each(cache.event[eventName], function(key, item) {
// 执行当前模块的全部事件
- if(filterName === '{*}'){
+ if (filterName === '{*}') {
layui.each(item, callback);
return;
}
@@ -783,20 +958,36 @@
return result;
};
- // 新增模块事件
- Layui.prototype.on = function(events, modName, callback){
+ /**
+ * 新增模块事件
+ * @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);
}
- // 移除模块事件
- Layui.prototype.off = function(events, modName){
+ /**
+ * 移除模块事件
+ * @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);
};
- // 防抖
- Layui.prototype.debounce = function (func, wait) {
+ /**
+ * 防抖
+ * @param {Function} func - 回调
+ * @param {number} wait - 延时执行的毫秒数
+ * @returns {Function}
+ */
+ Class.prototype.debounce = function (func, wait) {
var timeout;
return function () {
var context = this;
@@ -808,8 +999,12 @@
}
};
- // 节流
- Layui.prototype.throttle = function (func, wait) {
+ /**
+ * 节流
+ * @param {Function} func - 回调
+ * @param {number} wait - 不重复执行的毫秒数
+ */
+ Class.prototype.throttle = function (func, wait) {
var cooldown = false;
return function () {
var context = this;
@@ -824,8 +1019,6 @@
}
};
- // exports layui
- win.layui = new Layui();
-
-}(window); // gulp build: layui-footer
-
+ // export layui
+ window.layui = new Class();
+})(window);