layui.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. /*!
  2. @Name: layui
  3. @Description:经典模块化前端 UI 框架
  4. @Homepage: www.layui.com
  5. @Author: 贤心
  6. @License:MIT
  7. */
  8. ;!function(win){
  9. "use strict";
  10. var doc = document, config = {
  11. modules: {} //记录模块物理路径
  12. ,status: {} //记录模块加载状态
  13. ,timeout: 10 //符合规范的模块请求最长等待秒数
  14. ,event: {} //记录模块自定义事件
  15. }
  16. ,Layui = function(){
  17. this.v = '2.5.6'; //版本号
  18. }
  19. //获取layui所在目录
  20. ,getPath = function(){
  21. var jsPath = doc.currentScript ? doc.currentScript.src : function(){
  22. var js = doc.scripts
  23. ,last = js.length - 1
  24. ,src;
  25. for(var i = last; i > 0; i--){
  26. if(js[i].readyState === 'interactive'){
  27. src = js[i].src;
  28. break;
  29. }
  30. }
  31. return src || js[last].src;
  32. }();
  33. return jsPath.substring(0, jsPath.lastIndexOf('/') + 1);
  34. }()
  35. //异常提示
  36. ,error = function(msg){
  37. win.console && console.error && console.error('Layui hint: ' + msg);
  38. }
  39. ,isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]'
  40. //内置模块
  41. ,modules = {
  42. layer: 'modules/layer' //弹层
  43. ,laydate: 'modules/laydate' //日期
  44. ,laypage: 'modules/laypage' //分页
  45. ,laytpl: 'modules/laytpl' //模板引擎
  46. ,layim: 'modules/layim' //web通讯
  47. ,layedit: 'modules/layedit' //富文本编辑器
  48. ,form: 'modules/form' //表单集
  49. ,upload: 'modules/upload' //上传
  50. ,transfer: 'modules/transfer' //上传
  51. ,tree: 'modules/tree' //树结构
  52. ,table: 'modules/table' //表格
  53. ,element: 'modules/element' //常用元素操作
  54. ,rate: 'modules/rate' //评分组件
  55. ,colorpicker: 'modules/colorpicker' //颜色选择器
  56. ,slider: 'modules/slider' //滑块
  57. ,carousel: 'modules/carousel' //轮播
  58. ,flow: 'modules/flow' //流加载
  59. ,util: 'modules/util' //工具块
  60. ,code: 'modules/code' //代码修饰器
  61. ,jquery: 'modules/jquery' //DOM库(第三方)
  62. ,mobile: 'modules/mobile' //移动大模块 | 若当前为开发目录,则为移动模块入口,否则为移动模块集合
  63. ,'layui.all': '../layui.all' //PC模块合并版
  64. };
  65. //记录基础数据
  66. Layui.prototype.cache = config;
  67. //定义模块
  68. Layui.prototype.define = function(deps, factory){
  69. var that = this
  70. ,type = typeof deps === 'function'
  71. ,callback = function(){
  72. var setApp = function(app, exports){
  73. layui[app] = exports;
  74. config.status[app] = true;
  75. };
  76. typeof factory === 'function' && factory(function(app, exports){
  77. setApp(app, exports);
  78. config.callback[app] = function(){
  79. factory(setApp);
  80. }
  81. });
  82. return this;
  83. };
  84. type && (
  85. factory = deps,
  86. deps = []
  87. );
  88. if((!layui['layui.all'] && layui['layui.mobile'])){
  89. return callback.call(that);
  90. }
  91. that.use(deps, callback);
  92. return that;
  93. };
  94. //使用特定模块
  95. Layui.prototype.use = function(apps, callback, exports){
  96. var that = this
  97. ,dir = config.dir = config.dir ? config.dir : getPath
  98. ,head = doc.getElementsByTagName('head')[0];
  99. apps = typeof apps === 'string' ? [apps] : apps;
  100. //如果页面已经存在 jQuery 1.7+ 库且所定义的模块依赖 jQuery,则不加载内部 jquery 模块
  101. if(window.jQuery && jQuery.fn.on){
  102. that.each(apps, function(index, item){
  103. if(item === 'jquery'){
  104. apps.splice(index, 1);
  105. }
  106. });
  107. layui.jquery = layui.$ = jQuery;
  108. }
  109. var item = apps[0]
  110. ,timeout = 0;
  111. exports = exports || [];
  112. //静态资源host
  113. config.host = config.host || (dir.match(/\/\/([\s\S]+?)\//)||['//'+ location.host +'/'])[0];
  114. //加载完毕
  115. function onScriptLoad(e, url){
  116. var readyRegExp = navigator.platform === 'PLaySTATION 3' ? /^complete$/ : /^(complete|loaded)$/
  117. if (e.type === 'load' || (readyRegExp.test((e.currentTarget || e.srcElement).readyState))) {
  118. config.modules[item] = url;
  119. head.removeChild(node);
  120. (function poll() {
  121. if(++timeout > config.timeout * 1000 / 4){
  122. return error(item + ' is not a valid module');
  123. };
  124. config.status[item] ? onCallback() : setTimeout(poll, 4);
  125. }());
  126. }
  127. }
  128. //回调
  129. function onCallback(){
  130. exports.push(layui[item]);
  131. apps.length > 1 ?
  132. that.use(apps.slice(1), callback, exports)
  133. : ( typeof callback === 'function' && callback.apply(layui, exports) );
  134. }
  135. //如果引入了完整库(layui.all.js),内置的模块则不必再加载
  136. if(apps.length === 0
  137. || (layui['layui.all'] && modules[item])
  138. || (!layui['layui.all'] && layui['layui.mobile'] && modules[item])
  139. ){
  140. return onCallback(), that;
  141. }
  142. //获取加载的模块 URL
  143. //如果是内置模块,则按照 dir 参数拼接模块路径
  144. //如果是扩展模块,则判断模块路径值是否为 {/} 开头,
  145. //如果路径值是 {/} 开头,则模块路径即为后面紧跟的字符。
  146. //否则,则按照 base 参数拼接模块路径
  147. var url = ( modules[item] ? (dir + 'lay/')
  148. : (/^\{\/\}/.test(that.modules[item]) ? '' : (config.base || ''))
  149. ) + (that.modules[item] || item) + '.js';
  150. url = url.replace(/^\{\/\}/, '');
  151. //如果扩展模块(即:非内置模块)对象已经存在,则不必再加载
  152. if(!config.modules[item] && layui[item]){
  153. config.modules[item] = url; //并记录起该扩展模块的 url
  154. }
  155. //首次加载模块
  156. if(!config.modules[item]){
  157. var node = doc.createElement('script');
  158. node.async = true;
  159. node.charset = 'utf-8';
  160. node.src = url + function(){
  161. var version = config.version === true
  162. ? (config.v || (new Date()).getTime())
  163. : (config.version||'');
  164. return version ? ('?v=' + version) : '';
  165. }();
  166. head.appendChild(node);
  167. if(node.attachEvent && !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && !isOpera){
  168. node.attachEvent('onreadystatechange', function(e){
  169. onScriptLoad(e, url);
  170. });
  171. } else {
  172. node.addEventListener('load', function(e){
  173. onScriptLoad(e, url);
  174. }, false);
  175. }
  176. config.modules[item] = url;
  177. } else { //缓存
  178. (function poll() {
  179. if(++timeout > config.timeout * 1000 / 4){
  180. return error(item + ' is not a valid module');
  181. };
  182. (typeof config.modules[item] === 'string' && config.status[item])
  183. ? onCallback()
  184. : setTimeout(poll, 4);
  185. }());
  186. }
  187. return that;
  188. };
  189. //获取节点的style属性值
  190. Layui.prototype.getStyle = function(node, name){
  191. var style = node.currentStyle ? node.currentStyle : win.getComputedStyle(node, null);
  192. return style[style.getPropertyValue ? 'getPropertyValue' : 'getAttribute'](name);
  193. };
  194. //css外部加载器
  195. Layui.prototype.link = function(href, fn, cssname){
  196. var that = this
  197. ,link = doc.createElement('link')
  198. ,head = doc.getElementsByTagName('head')[0];
  199. if(typeof fn === 'string') cssname = fn;
  200. var app = (cssname || href).replace(/\.|\//g, '')
  201. ,id = link.id = 'layuicss-'+app
  202. ,timeout = 0;
  203. link.rel = 'stylesheet';
  204. link.href = href + (config.debug ? '?v='+new Date().getTime() : '');
  205. link.media = 'all';
  206. if(!doc.getElementById(id)){
  207. head.appendChild(link);
  208. }
  209. if(typeof fn !== 'function') return that;
  210. //轮询css是否加载完毕
  211. (function poll() {
  212. if(++timeout > config.timeout * 1000 / 100){
  213. return error(href + ' timeout');
  214. };
  215. parseInt(that.getStyle(doc.getElementById(id), 'width')) === 1989 ? function(){
  216. fn();
  217. }() : setTimeout(poll, 100);
  218. }());
  219. return that;
  220. };
  221. //存储模块的回调
  222. config.callback = {};
  223. //重新执行模块的工厂函数
  224. Layui.prototype.factory = function(modName){
  225. if(layui[modName]){
  226. return typeof config.callback[modName] === 'function'
  227. ? config.callback[modName]
  228. : null;
  229. }
  230. };
  231. //css内部加载器
  232. Layui.prototype.addcss = function(firename, fn, cssname){
  233. return layui.link(config.dir + 'css/' + firename, fn, cssname);
  234. };
  235. //图片预加载
  236. Layui.prototype.img = function(url, callback, error) {
  237. var img = new Image();
  238. img.src = url;
  239. if(img.complete){
  240. return callback(img);
  241. }
  242. img.onload = function(){
  243. img.onload = null;
  244. typeof callback === 'function' && callback(img);
  245. };
  246. img.onerror = function(e){
  247. img.onerror = null;
  248. typeof error === 'function' && error(e);
  249. };
  250. };
  251. //全局配置
  252. Layui.prototype.config = function(options){
  253. options = options || {};
  254. for(var key in options){
  255. config[key] = options[key];
  256. }
  257. return this;
  258. };
  259. //记录全部模块
  260. Layui.prototype.modules = function(){
  261. var clone = {};
  262. for(var o in modules){
  263. clone[o] = modules[o];
  264. }
  265. return clone;
  266. }();
  267. //拓展模块
  268. Layui.prototype.extend = function(options){
  269. var that = this;
  270. //验证模块是否被占用
  271. options = options || {};
  272. for(var o in options){
  273. if(that[o] || that.modules[o]){
  274. error('\u6A21\u5757\u540D '+ o +' \u5DF2\u88AB\u5360\u7528');
  275. } else {
  276. that.modules[o] = options[o];
  277. }
  278. }
  279. return that;
  280. };
  281. // location.hash 路由解析
  282. Layui.prototype.router = function(hash){
  283. var that = this
  284. ,hash = hash || location.hash
  285. ,data = {
  286. path: []
  287. ,search: {}
  288. ,hash: (hash.match(/[^#](#.*$)/) || [])[1] || ''
  289. };
  290. if(!/^#\//.test(hash)) return data; //禁止非路由规范
  291. hash = hash.replace(/^#\//, '');
  292. data.href = '/' + hash;
  293. hash = hash.replace(/([^#])(#.*$)/, '$1').split('/') || [];
  294. //提取 Hash 结构
  295. that.each(hash, function(index, item){
  296. /^\w+=/.test(item) ? function(){
  297. item = item.split('=');
  298. data.search[item[0]] = item[1];
  299. }() : data.path.push(item);
  300. });
  301. return data;
  302. };
  303. //URL 解析
  304. Layui.prototype.url = function(href){
  305. var that = this
  306. ,data = {
  307. //提取 url 路径
  308. pathname: function(){
  309. var pathname = href
  310. ? function(){
  311. var pathUrl = (href.match(/\.[^.]+?\/.+/) || [])[0] || '';
  312. return pathUrl.replace(/^[^\/]+/, '').replace(/\?.+/, '');
  313. }()
  314. : location.pathname;
  315. return pathname.replace(/^\//, '').split('/');
  316. }()
  317. //提取 url 参数
  318. ,search: function(){
  319. var obj = {}
  320. ,search = (href
  321. ? ((href.match(/\?.+/) || [])[0] || '')
  322. : location.search
  323. ).replace(/^\?+/, '').split('&'); //去除 ?,按 & 分割参数
  324. //遍历分割后的参数
  325. that.each(search, function(index, item){
  326. var _index = item.indexOf('=')
  327. ,key = function(){ //提取 key
  328. if(_index < 0){
  329. return item.substr(0, item.length);
  330. } else if(_index === 0){
  331. return false;
  332. } else {
  333. return item.substr(0, _index);
  334. }
  335. }();
  336. //提取 value
  337. if(key){
  338. obj[key] = _index > 0 ? item.substr(_index + 1) : null;
  339. }
  340. });
  341. return obj;
  342. }()
  343. //提取 Hash
  344. ,hash: that.router(function(){
  345. return href
  346. ? ((href.match(/#.+/) || [])[0] || '')
  347. : location.hash;
  348. }())
  349. };
  350. return data;
  351. };
  352. //本地持久性存储
  353. Layui.prototype.data = function(table, settings, storage){
  354. table = table || 'layui';
  355. storage = storage || localStorage;
  356. if(!win.JSON || !win.JSON.parse) return;
  357. //如果settings为null,则删除表
  358. if(settings === null){
  359. return delete storage[table];
  360. }
  361. settings = typeof settings === 'object'
  362. ? settings
  363. : {key: settings};
  364. try{
  365. var data = JSON.parse(storage[table]);
  366. } catch(e){
  367. var data = {};
  368. }
  369. if('value' in settings) data[settings.key] = settings.value;
  370. if(settings.remove) delete data[settings.key];
  371. storage[table] = JSON.stringify(data);
  372. return settings.key ? data[settings.key] : data;
  373. };
  374. //本地会话性存储
  375. Layui.prototype.sessionData = function(table, settings){
  376. return this.data(table, settings, sessionStorage);
  377. }
  378. //设备信息
  379. Layui.prototype.device = function(key){
  380. var agent = navigator.userAgent.toLowerCase()
  381. //获取版本号
  382. ,getVersion = function(label){
  383. var exp = new RegExp(label + '/([^\\s\\_\\-]+)');
  384. label = (agent.match(exp)||[])[1];
  385. return label || false;
  386. }
  387. //返回结果集
  388. ,result = {
  389. os: function(){ //底层操作系统
  390. if(/windows/.test(agent)){
  391. return 'windows';
  392. } else if(/linux/.test(agent)){
  393. return 'linux';
  394. } else if(/iphone|ipod|ipad|ios/.test(agent)){
  395. return 'ios';
  396. } else if(/mac/.test(agent)){
  397. return 'mac';
  398. }
  399. }()
  400. ,ie: function(){ //ie版本
  401. return (!!win.ActiveXObject || "ActiveXObject" in win) ? (
  402. (agent.match(/msie\s(\d+)/) || [])[1] || '11' //由于ie11并没有msie的标识
  403. ) : false;
  404. }()
  405. ,weixin: getVersion('micromessenger') //是否微信
  406. };
  407. //任意的key
  408. if(key && !result[key]){
  409. result[key] = getVersion(key);
  410. }
  411. //移动设备
  412. result.android = /android/.test(agent);
  413. result.ios = result.os === 'ios';
  414. result.mobile = (result.android || result.ios) ? true : false;
  415. return result;
  416. };
  417. //提示
  418. Layui.prototype.hint = function(){
  419. return {
  420. error: error
  421. }
  422. };
  423. //遍历
  424. Layui.prototype.each = function(obj, fn){
  425. var key
  426. ,that = this;
  427. if(typeof fn !== 'function') return that;
  428. obj = obj || [];
  429. if(obj.constructor === Object){
  430. for(key in obj){
  431. if(fn.call(obj[key], key, obj[key])) break;
  432. }
  433. } else {
  434. for(key = 0; key < obj.length; key++){
  435. if(fn.call(obj[key], key, obj[key])) break;
  436. }
  437. }
  438. return that;
  439. };
  440. //将数组中的对象按其某个成员排序
  441. Layui.prototype.sort = function(obj, key, desc){
  442. var clone = JSON.parse(
  443. JSON.stringify(obj || [])
  444. );
  445. if(!key) return clone;
  446. //如果是数字,按大小排序,如果是非数字,按字典序排序
  447. clone.sort(function(o1, o2){
  448. var isNum = /^-?\d+$/
  449. ,v1 = o1[key]
  450. ,v2 = o2[key];
  451. if(isNum.test(v1)) v1 = parseFloat(v1);
  452. if(isNum.test(v2)) v2 = parseFloat(v2);
  453. if(v1 && !v2){
  454. return 1;
  455. } else if(!v1 && v2){
  456. return -1;
  457. }
  458. if(v1 > v2){
  459. return 1;
  460. } else if (v1 < v2) {
  461. return -1;
  462. } else {
  463. return 0;
  464. }
  465. });
  466. desc && clone.reverse(); //倒序
  467. return clone;
  468. };
  469. //阻止事件冒泡
  470. Layui.prototype.stope = function(thisEvent){
  471. thisEvent = thisEvent || win.event;
  472. try { thisEvent.stopPropagation() } catch(e){
  473. thisEvent.cancelBubble = true;
  474. }
  475. };
  476. //自定义模块事件
  477. Layui.prototype.onevent = function(modName, events, callback){
  478. if(typeof modName !== 'string'
  479. || typeof callback !== 'function') return this;
  480. return Layui.event(modName, events, null, callback);
  481. };
  482. //执行自定义模块事件
  483. Layui.prototype.event = Layui.event = function(modName, events, params, fn){
  484. var that = this
  485. ,result = null
  486. ,filter = events.match(/\((.*)\)$/)||[] //提取事件过滤器字符结构,如:select(xxx)
  487. ,eventName = (modName + '.'+ events).replace(filter[0], '') //获取事件名称,如:form.select
  488. ,filterName = filter[1] || '' //获取过滤器名称,,如:xxx
  489. ,callback = function(_, item){
  490. var res = item && item.call(that, params);
  491. res === false && result === null && (result = false);
  492. };
  493. //添加事件
  494. if(fn){
  495. config.event[eventName] = config.event[eventName] || {};
  496. //这里不再对多次事件监听做支持,避免更多麻烦
  497. //config.event[eventName][filterName] ? config.event[eventName][filterName].push(fn) :
  498. config.event[eventName][filterName] = [fn];
  499. return this;
  500. }
  501. //执行事件回调
  502. layui.each(config.event[eventName], function(key, item){
  503. //执行当前模块的全部事件
  504. if(filterName === '{*}'){
  505. layui.each(item, callback);
  506. return;
  507. }
  508. //执行指定事件
  509. key === '' && layui.each(item, callback);
  510. (filterName && key === filterName) && layui.each(item, callback);
  511. });
  512. return result;
  513. };
  514. win.layui = new Layui();
  515. }(window);