├── .gitignore ├── README.md ├── examples ├── demo1 │ ├── base.js │ ├── base.tpl │ ├── base.tpl.js │ └── index.html ├── demo2 │ └── index.html ├── index.html ├── lib │ ├── dialog │ │ ├── base.css │ │ ├── base.js │ │ ├── base.tpl │ │ └── base.tpl.js │ └── jquery.js └── seajs │ ├── sea-config.js │ ├── sea-style.js │ └── sea.js ├── index.js ├── lib ├── alias.js ├── cache.js ├── debug.js ├── default.js ├── load │ ├── css.js │ ├── css_js.js │ ├── file.js │ ├── js.js │ └── tpl_js.js ├── main.js ├── mime.json ├── url.js └── util.js ├── nginx.conf ├── package.json └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /demo -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-combo 2 | 3 | nodejs的静态合并 4 | 5 | ## 故事 6 | 7 | > [http://www.xuexb.com/html/250.html](http://www.xuexb.com/html/250.html) 8 | 9 | 由于项目迭代开发,可能一直在改版, 如果使用`grunt`之类的工作进行合并,又有学习成本和维护成本, 但如果不压吧, 感觉B格又不高, 于是想想有没有一种可以在请求的时候去合并,压缩的, 起初看到大搜车的`ads`, 后来知道了美丽说也是这种模式, 于是就想试试用`nodejs`写一个类似这么个东东...然后... 10 | 11 | > 只是想学学`nodejs`, 只是把我的想法实践出来罢了! 12 | 13 | ## 期望 14 | 15 | 最终要完成合并`cmd`模块, 普通文件, `tpl->js`, `css->js`, 配合`CDN`缓存使用达到上线不费劲的效果! 16 | 17 | ## 介绍 18 | 19 | 她是一个可以被动式合并你设置的资源, 让你可以抛弃那些略为复杂的打包, 上线机制, 从而达到**开发人员只需要关心自己代码**的境界! 20 | 21 | ### 普通的合并 22 | 23 | 这种通常可以满足你的需求了,可以把多个文件进行合并(前提是一个链接只能合并一种文件类型), 比如 `demo.com/??test/a.css,test/b.css`, 也或者 `demo.com/??test/a.js,test/b.js`, 但由于文件路径很长的原因导致整个链接可能很长,很长... 24 | 25 | 然后就在考虑是不是可以以子目录的方式进行合并? 26 | 27 | ### 子目录合并 28 | 29 | 这种可以使你先进入深的目录,再动态的合并文件, 比如 `demo.com/test/test2/test3??a.css,b.css,c.css` ,是不是略爽? 30 | 31 | 但如果一个链接里我目录既然深, 还会跨目录呢? 32 | 33 | ### 跨目录合并 34 | 35 | 先上"比如": `demo.com/test/test2/test3/??a.css,b.css,/biede/c.css`, 这种就可以解决跨目录的问题, 但我在想很反感, 通过`seajs`的`alias`我在考虑链接能不能也"别名", 于是... 36 | 37 | ### 别名加载 38 | 39 | 例子: 40 | 41 | ``` js 42 | alias: { 43 | 'global': 'src/??seajs/sea,seajs/sea-style,seajs/sea-config,lib/dialog/base' 44 | }, 45 | ``` 46 | 47 | `demo.com/??global` 就相当于 `demo.com/src/??seajs/sea,seajs/sea-style,seajs/sea-config,lib/dialog/base` 48 | 49 | ### cmd和非cmd 50 | 51 | 会判断js文件内是否有`define`关键字, 如果有则视为`cmd`文件, 可以添加`file_no_cmd`来显式的设置不是`cmd` 52 | 53 | ### 无缓存加载 54 | 55 | 正如你所想, 添加`?`, 如: 56 | 57 | * `demo.com/??a.css,b.css?v=123` 58 | * `demo.com/??a.css,b.css?34234234` 59 | * `demo.com/src/xxoo??a.css,b.css?v=123` 60 | * `demo.com/src/xxoo/??a,b?v=123&type=css` 61 | * `demo.com/src/xxoo/??a,b?v=123&fdsfsd=1` 62 | * `demo.com/??别名?v=123` 63 | 64 | ## demo 65 | 66 | ### 合并css 67 | 68 | * `demo.com/??test/a.css,test/b.css` 普通的合并 69 | * `demo.com/test/??a.css,b.css` 以子目录合并 70 | * `demo.com/test/??a,b?type=css` 添加文件类型,可以替代文件后缀 71 | * `demo.com/??global` 别名方式 72 | * `demo.com/??global?dfdfdf` 别名方式 73 | * `demo.com/test/??a.css,b.css,/test2/c.css` 跨目录合并 74 | * `demo.com/test/??a.css,b.css?4654` 添加时间缀 75 | * `demo.com/test/??a.css,b?4654` 在没有显式的设置过`type`时省略的会补上第一个的类型 76 | 77 | 78 | ### 合并js 79 | 80 | **注:js会判断是否为`cmd`,如果是则会添加id并抽取依赖, uri是以子目录为基础来设置** 81 | 82 | * `demo.com/??test/a.js,test/b.js` 普通的合并 83 | * `demo.com/test/??a.js,b.js` 以子目录合并 84 | * `demo.com/test/??a,b?type=js` 添加文件类型,可以替代文件后缀,默认`type`为`js`,如果没有显式的设置过`type`,则是以第一个文件的后缀为标准 85 | * `demo.com/test/??a,b` 默认文件类型 86 | * `demo.com/??global` 别名方式 87 | 88 | ## 请求处理流程 89 | 90 | 在捕获到页面请求后,经过这些处理: 91 | 92 | 1. 判断是否有`??`存在,如果没有则认为需要`nginx`等服务器处理 93 | 2. 判断`??`后的字符是否为别名(配置的`alias`), 如果有,则拿别名的值替换整个`url` 94 | 3. 判断是否开启`cache`, 如果有则使用`md5`的`url`读取缓存文件是否存在,如果存在,则直接输出并中断 95 | 4. 对`url`进行分组,生成文件路径数组 96 | 5. 遍历路径数组,判断文件是否存在,存在则调用相应的处理文件进行处理 97 | 6. 处理文件,如`cmd`的生成 98 | 7. 如果开启`cache`则写入缓存 99 | 8. 数据输出 100 | 101 | 102 | ## Nginx代理 103 | 104 | 建议使用`nginx`做`http`代理,如果请求中包含`??`才交给`node-combo`处理,配置如下: 105 | 106 | ``` 107 | server { 108 | listen 80; 109 | server_name 127.0.0.1; 110 | 111 | 112 | location / { 113 | root 目标路径; 114 | index index.html index.htm; 115 | autoindex on; 116 | } 117 | 118 | if ($request_uri ~* "\?\?"){ 119 | rewrite (.*) /__combo; 120 | } 121 | 122 | 123 | location /__combo { 124 | proxy_set_header Connection ""; 125 | proxy_set_header Host $http_host; 126 | proxy_set_header X-NginX-Proxy true; 127 | proxy_pass http://127.0.0.1:90$request_uri; 这里是node-combo跑的路径 128 | proxy_redirect off; 129 | } 130 | } 131 | ``` 132 | 133 | ## 配置 134 | 135 | ``` js 136 | { 137 | base: './',//根路径 138 | cache: true,//是否开启缓存 139 | cache_path: './__cache',//缓存目录 140 | alias: {},//别名 141 | port: 80,//端口 142 | } 143 | ``` 144 | 145 | ## 使用 146 | 147 | `npm install node-combo` 148 | 149 | ``` js 150 | var combo = require('node-combo'); 151 | 152 | combo( [option] ); 153 | ``` 154 | 155 | ## bug 156 | 157 | [提交](https://github.com/xuexb/node-combo/issues) 158 | 159 | ## changelog 160 | 161 | ### 2.0.2 162 | 修改依赖 163 | 164 | ### 2.0.1 165 | 166 | * 文档完善 167 | * 支持在没有显式的设置`type`时使用第1个文件后缀为类型 168 | 169 | ### 2.0.0 170 | 171 | * 完成基本的功能并发布到`npm`上 172 | 173 | ## todo 174 | 175 | * demo 176 | * tpl->js的支持 177 | * css->js的支持 178 | * debug功能 179 | * cache管理 180 | * 压缩开关 181 | * jsHint 182 | * Etag?? CDN?? -------------------------------------------------------------------------------- /examples/demo1/base.js: -------------------------------------------------------------------------------- 1 | define(function(require) { 2 | var $ = require('../lib/jquery'), 3 | Dialog = require('../lib/dialog/base'); 4 | 5 | var html = require('./base.tpl'); 6 | 7 | var demo = { 8 | 'default': function() { 9 | new Dialog({ 10 | content: '我是测试的', 11 | title: '测试', 12 | width: 400, 13 | height: 200 14 | }); 15 | }, 16 | alert: function() { 17 | Dialog.alert('我是alert'); 18 | }, 19 | confirm: function() { 20 | Dialog.confirm('确认?', function() { 21 | 22 | }); 23 | }, 24 | success: function() { 25 | Dialog.success('你成功了'); 26 | }, 27 | error: function() { 28 | Dialog.error('一会再试吧'); 29 | } 30 | } 31 | 32 | $('h1').text('加载完成,耗时:'+ (new Date().getTime() - __load) + 'ms'); 33 | 34 | $('.box').html(html).on('click', 'button', function() { 35 | var fn = demo[this.getAttribute('data-type')]; 36 | 37 | if(!fn){ 38 | fn = demo['default']; 39 | } 40 | 41 | fn(); 42 | }); 43 | }); -------------------------------------------------------------------------------- /examples/demo1/base.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/demo1/base.tpl.js: -------------------------------------------------------------------------------- 1 | define([], '\ 2 | \ 3 | \ 4 | \ 5 | \ 6 | '); -------------------------------------------------------------------------------- /examples/demo1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 默认异步加载 6 | 9 | 10 | 11 | 12 |

13 | 加载中... 14 |

15 | 16 |
17 | 18 |

19 | 注: 因seajs加载tpl跨域问题,要先把tpl文件转换为js 20 |

21 | 22 |

23 | 这个demo只是加载一个自己的js,这个js里加载jquery和一个弹层插件(包括js,css,tpl),完成页面逻辑 24 |

25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | -------------------------------------------------------------------------------- /examples/demo2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 同步加载 6 | 9 | 10 | 11 | 12 |

13 | 加载中... 14 |

15 | 16 |
17 | 18 | 19 |

20 | 同步demo1版本 21 |

22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Demo 6 | 7 | 8 | 9 |

Demo

10 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/lib/dialog/base.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | 4 | /*弹出层*/ 5 | 6 | .ui-dialog-tips-icon,.ui-dialog-close{ 7 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAA8CAYAAABmdppWAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NzBBMjAxMUE1OTkzMTFFNDg5RjZBQjYxMUQ3OEQxN0YiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NzBBMjAxMUI1OTkzMTFFNDg5RjZBQjYxMUQ3OEQxN0YiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3MEEyMDExODU5OTMxMUU0ODlGNkFCNjExRDc4RDE3RiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3MEEyMDExOTU5OTMxMUU0ODlGNkFCNjExRDc4RDE3RiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqIotjEAAAIVSURBVHja7Je7SgNBFIY3cUtJ6SUkYgq7IEGxEOwiIhJQEzVgYWNjaTS1TyAx4BtYqrlYBNGYJxAtItgqGi+1IAhq9JzlDAzrzOy4m0JhD/yQuX3ssnv+PxsoP68akoqCZkFZUAL0CroAHYOqoJboUFAwFwadge5Au6AJUDeoFzRDc/e0J+wExAMPoKThXEnaOyMD4kLN+H3VeCgDRlzCeGiEB5YN71VlwEHQmAtAETTAjUdBMQRmXMI2QTnbfAaBaZewbQFwHoFxwQtdlMB2FDCs4SB1AF8bdKgogOUVMKtM0BWon5tjmxH6RWMtGFQTgaegKdsCO4SQOuhEA4ZVwVsuSRZzBKlrwrBKCLwFnSug75owdKIb1inpDnTKHN966G0pD7AU88egrcGXXMCyvLHY/fCAXKOhAWpQE+zb30N7oWlOkmksUAyM0Nol6Ah0SA/z54u9NX0tu4JP0Bv9blNHvdDch+xQIJ4YF2XKnkYM4C2vgB79TBFmisoTRfbvmCkyT1TZv5UppiRTeE/EWtewfytTTIUx2KGGA8zKFFOQKSqoky8KM8VTiTJFFkwsEgzFVUozRZVyKmilq6cv+sQ9RScYNn6I1kI05mtNlikFxQNg4VUQZQpzmwj9K/VS2F0tP1M6kCnNoVhnM6W9uOxnip8pfqb8/0yRfafIYMz+87RH+zvFfaaQ2/iZ8ocy5VuAAQCfH9Kq9FtUMgAAAABJRU5ErkJggg=="); 8 | background-repeat:no-repeat; 9 | } 10 | 11 | 12 | .ui-dialog-outer, 13 | .ui-dialog-outer table, 14 | .ui-dialog-outer p{ 15 | padding:0;margin:0; 16 | } 17 | 18 | /*弹出层*/ 19 | .ui-dialog-outer { text-align:left; outline:none;font-size:14px;position:relative;} 20 | .ui-dialog-border{ border:0 none; margin:0; border-collapse:collapse;} 21 | /*.ui-dialog-header, .ui-dialog-footer { padding:0; }*/ 22 | .ui-dialog-iframe .ui-dialog-content{height:100%;background:#fff url('http://static.jiapai.cc/res/img/loading-32-32.gif') center center no-repeat;} 23 | .ui-dialog-iframe iframe{width: 100%;height: 100%;} 24 | /*标题栏*/ 25 | .ui-dialog-header{ position:relative;/* height:36px; */ z-index:100;} 26 | .ui-dialog-title { overflow:hidden;cursor:default;height:36px; line-height:36px;color:#333;padding:0 20px;background-color:#f2f2f2;} 27 | .ui-dialog-close{display:block; position: absolute;overflow:hidden;right: 10px;top:8px;height:20px;width: 20px;text-indent:-9999em;cursor:pointer;background-position:0 -20px;} 28 | .ui-dialog-close:hover{text-decoration:none;} 29 | /*loading*/ 30 | .ui-dialog-loading {width:80px;height: 50px;overflow: hidden;background: url('http://static.jiapai.cc/res/img/loading-32-32.gif') no-repeat 30px center;line-height: 50px;padding-left: 80px;font-size: 16px;} 31 | /*按钮组*/ 32 | .ui-dialog-buttons{padding:20px; text-align:center; white-space:nowrap;font-size:0;} 33 | .ui-dialog-button{user-select:none;-moz-user-select: none;-webkit-user-select: none; -ms-user-select: none;margin-left:20px;cursor: pointer; display: inline-block; text-align: center;height:30px;line-height:30px;padding:0 20px;width:auto;background-color:#adaeb0;color:#fff;font-size:14px;border-radius:3px;text-decoration:none;} 34 | .ui-dialog-buttons a:first-child{margin-left:0;} 35 | .ui-dialog-button:hover{background-color:#999;text-decoration:none;} 36 | /*无效按钮*/ 37 | .ui-dialog-button-disabled,.ui-dialog-button-disabled:hover,.ui-dialog-button-disabled:active{cursor:default;color:#666;background-color:#ddd;filter:alpha(opacity=50); opacity:.5;text-decoration:none;} 38 | /*高亮*/ 39 | .ui-dialog-button-on{background-color:#fe555a;color:#fff;} 40 | .ui-dialog-button-on:hover{background-color:#e64348;color:#fff;} 41 | /*遮罩*/ 42 | .ui-dialog-mask { background-color:#000; filter:alpha(opacity=30); opacity:0.3; } 43 | /*内部一个边框*/ 44 | .ui-dialog-inner{background-color:#fff;border:1px solid #bab0b8; } 45 | /*没有标题*/ 46 | .ui-dialog-noTitle .ui-dialog-title { display:none; } 47 | /*视觉*/ 48 | @media screen and (min-width:0) { 49 | .ui-dialog-outer { -webkit-transform: scale(0); transform: scale(0); -webkit-transition: -webkit-transform .2s ease-in-out; transition: transform .2s ease-in-out; } 50 | .ui-dialog-show .ui-dialog-outer{ -webkit-transform: scale(1); transform: scale(1); } 51 | } 52 | /*扩展开始*/ 53 | /*alert*/ 54 | .ui-dialog-alert .ui-dialog-content{text-align:center;padding-top:30px;padding-bottom:20px;font-size:14px;} 55 | .ui-dialog-alert.ui-dialog-noTitle .ui-dialog-content{padding-top:40px;} 56 | .ui-dialog-alert .ui-dialog-inner{border:none;} 57 | 58 | 59 | /*简单提示*/ 60 | .ui-dialog-tips .ui-dialog-inner{ 61 | border-color:#898989; 62 | } 63 | .ui-dialog-tips .ui-dialog-content{padding:18px 30px 18px 46px;color:#333;} 64 | .ui-dialog-tips .ui-dialog-header{display:none;} 65 | .ui-dialog-tips-icon{position:absolute;left:16px;top:20px;overflow:hidden;display:block;width:20px;height:20px;} 66 | .ui-dialog-tips-success-icon{background-position:0 0;} 67 | .ui-dialog-tips-error-icon{background-position:0 -40px;} 68 | 69 | /*fix*/ 70 | .ui-dialog-content,.ui-dialog-inner{position:relative;} -------------------------------------------------------------------------------- /examples/lib/dialog/base.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 弹出层插件 3 | * @namespace dialog 4 | */ 5 | 6 | 7 | 8 | define(function(require) { 9 | 'use strict'; 10 | 11 | var jQuery = require('../jquery'); 12 | 13 | var _templates = require('./base.tpl'); 14 | 15 | require('./base.css'); 16 | 17 | 18 | return (function(window, document, $, undefined) { 19 | 20 | var _count = 0, 21 | $window = $(window), 22 | $document = $(document), 23 | _expando = 'Dialog' + (+new Date()); 24 | 25 | /** 26 | * 弹出层构架函数 27 | */ 28 | var Dialog = function(config, ok, cancel) { 29 | var api; 30 | config = config || {}; //如果没有配置参数 31 | 32 | // 合并 33 | config = $.extend(true, {}, Dialog.defaults, config); 34 | 35 | //如果已经弹出, id为唯一标识 36 | config.id = config.id || _expando + _count; 37 | api = Dialog.list[config.id]; 38 | if (api) { //如果缓存里有 39 | return api.zIndex(); //去操作焦点.focus();//置顶该实例并返回 40 | } 41 | 42 | 43 | //如果是写的string 44 | if (typeof config === 'string') { 45 | config = { 46 | content: config 47 | } 48 | } 49 | 50 | 51 | // 52 | if (!$.isArray(config.button)) { //如果参数的button不是数组则让其为数组,因为后面要进行push操作追加 53 | config.button = []; 54 | } 55 | 56 | 57 | // 确定按钮 58 | if (ok !== undefined) { 59 | config.ok = ok; 60 | } 61 | 62 | if (config.ok) { //如果有确认按钮则追加到button数组里 63 | config.button.push({ 64 | id: 'ok', 65 | value: config.okValue, 66 | callback: config.ok, 67 | focus: true, //确认按钮默认为聚焦状态 68 | highlight: true //高亮 69 | }); 70 | } 71 | 72 | 73 | // 取消按钮 74 | if (cancel !== undefined) { 75 | config.cancel = cancel; 76 | } 77 | 78 | if (config.cancel) { //如果有取消按钮则追加到button数组里 79 | config.button.push({ 80 | id: 'cancel', 81 | value: config.cancelValue, 82 | callback: config.cancel 83 | }); 84 | } 85 | 86 | // 更新 zIndex 全局配置 87 | Dialog.defaults.zIndex = config.zIndex; //把参数里的zindex更新到全局对象里 88 | 89 | _count++; //让标识+1,防止重复 90 | Dialog.list[config.id] = this; 91 | return this._create(config); 92 | } 93 | 94 | Dialog.version = '6.0'; //版本 95 | 96 | Dialog.prototype = { //采用jQuery无需new返回新实例 97 | /** 98 | * 内部触发事件 99 | */ 100 | _trigger: function(type) { 101 | var self = this, 102 | listeners = self._getEventListener(type), 103 | i = 0, 104 | len = listeners.length; 105 | 106 | for (;i < len; i++) { 107 | listeners[i].call(self); 108 | } 109 | }, 110 | 111 | /** 112 | * 添加事件 113 | * @param {String} 事件类型 114 | * @param {Function} 监听函数 115 | */ 116 | on: function(type, callback) { 117 | var self = this; 118 | 119 | if (type.indexOf('button:') === 0) { 120 | type = type.slice(7); 121 | if (!self._callback[type]) { 122 | self._callback[type] = {}; 123 | } 124 | self._callback[type].callback = callback; 125 | } else { 126 | self._getEventListener(type).push(callback); 127 | } 128 | return self; 129 | }, 130 | 131 | 132 | /** 133 | * 删除事件 134 | * @param {String} 事件类型 135 | * @param {Function} 监听函数 136 | */ 137 | off: function(type, callback) { 138 | var self = this, 139 | listeners, 140 | i; 141 | 142 | if (type.indexOf('button:') === 0) { 143 | delete self._callback[type.slice(7)]; 144 | } else { 145 | listeners = self._getEventListener(type); 146 | if ('function' === typeof callback) { 147 | for (i = 0; i < listeners.length; i++) { 148 | if (callback === listeners[i]) { 149 | listeners.splice(i--, 1); 150 | } 151 | } 152 | } else { 153 | listeners.length = 0; 154 | } 155 | } 156 | 157 | 158 | 159 | return self; 160 | }, 161 | 162 | 163 | // 获取事件缓存 164 | _getEventListener: function(type) { 165 | var listener = this._listener; 166 | if (!listener[type]) { 167 | listener[type] = []; 168 | } 169 | return listener[type]; 170 | }, 171 | 172 | _create: function(config) { 173 | var self = this; 174 | 175 | self._listener = {}; //v6事件空间 176 | self._callback = {}; //按钮事件空间 177 | self._dom = {}; //jQuery对象 178 | // self.visibled = true; 179 | // self.locked = false;// 180 | // self.closed = false;//设置关闭标识 181 | self.config = config; //把参数引用到对象上 182 | 183 | self._createHTML(config); //输出html 184 | 185 | 186 | //添加对iframe的支持 187 | if (config.url) { 188 | self._$('wrap').addClass('ui-dialog-iframe'); 189 | 190 | self._dom.iframe = $('