1 /** 2 * @fileOverview use and attach mod 3 * @author yiminghe@gmail.com,lifesinger@gmail.com 4 */ 5 (function (S) { 6 if (typeof require !== 'undefined') { 7 return; 8 } 9 10 var Loader = S.Loader, 11 data = Loader.STATUS, 12 utils = Loader.Utils, 13 INIT = data.INIT, 14 IE = utils.IE, 15 win = S.Env.host, 16 LOADING = data.LOADING, 17 LOADED = data.LOADED, 18 ERROR = data.ERROR, 19 ALL_REQUIRES = "__allRequires", 20 CURRENT_MODULE = "__currentModule", 21 ATTACHED = data.ATTACHED; 22 23 S.augment(Loader, { 24 /** 25 * Start load specific mods, and fire callback when these mods and requires are attached. 26 * @example 27 * <code> 28 * S.use('mod-name', callback, config); 29 * S.use('mod1,mod2', callback, config); 30 * </code> 31 * @param {String|String[]} modNames names of mods to be loaded,if string then separated by space 32 * @param {Function} callback callback when modNames are all loaded, 33 * with KISSY as first argument and mod's value as the following arguments 34 */ 35 use:function (modNames, callback) { 36 var self = this, 37 SS = self.SS; 38 39 modNames = utils.getModNamesAsArray(modNames); 40 modNames = utils.normalizeModNamesWithAlias(SS, modNames); 41 42 var normalizedModNames = utils.unalias(SS, modNames), 43 count = normalizedModNames.length, 44 currentIndex = 0; 45 46 function end() { 47 var mods = utils.getModules(SS, modNames); 48 callback && callback.apply(SS, mods); 49 } 50 51 // 已经全部 attached, 直接执行回调即可 52 if (utils.isAttached(SS, normalizedModNames)) { 53 return end(); 54 } 55 56 // 有尚未 attached 的模块 57 S.each(normalizedModNames, function (modName) { 58 // 从 name 开始调用,防止不存在模块 59 attachModByName(self, modName, function () { 60 currentIndex++; 61 if (currentIndex == count) { 62 end(); 63 } 64 }); 65 }); 66 67 return self; 68 } 69 }); 70 71 // 加载指定模块名模块,如果不存在定义默认定义为内部模块 72 function attachModByName(self, modName, callback) { 73 var SS = self.SS, mod; 74 utils.createModuleInfo(SS, modName); 75 mod = SS.Env.mods[modName]; 76 if (mod.status === ATTACHED) { 77 callback(); 78 return; 79 } 80 attachModRecursive(self, mod, callback); 81 } 82 83 84 /** 85 * Attach a module and all required modules. 86 */ 87 function attachModRecursive(self, mod, callback) { 88 var SS = self.SS, 89 r, 90 rMod, 91 i, 92 callbackBeCalled = 0, 93 // 最终有效的 require ,add 处声明为准 94 newRequires, 95 mods = SS.Env.mods; 96 97 // 复制一份当前的依赖项出来,防止 add 后修改! 98 // 事先配置的 require ,同 newRequires 有区别 99 var requires = utils.normalizeModNames(SS, mod.requires, mod.name); 100 101 /** 102 * check cyclic dependency between mods 103 * @private 104 */ 105 function cyclicCheck() { 106 // one mod's all requires mods to run its callback 107 var __allRequires = mod[ALL_REQUIRES] = mod[ALL_REQUIRES] || {}, 108 myName = mod.name, 109 rmod, 110 r__allRequires; 111 112 S.each(requires, function (r) { 113 rmod = mods[r]; 114 __allRequires[r] = 1; 115 if (rmod && (r__allRequires = rmod[ALL_REQUIRES])) { 116 S.mix(__allRequires, r__allRequires); 117 } 118 }); 119 120 if (__allRequires[myName]) { 121 S.log(__allRequires, "error"); 122 var JSON = win.JSON, 123 error = ""; 124 if (JSON) { 125 error = JSON.stringify(__allRequires); 126 } 127 S.error("find cyclic dependency by mod " + myName + " between mods : " + error); 128 } 129 } 130 131 S.log(cyclicCheck()); 132 133 // attach all required modules 134 for (i = 0; i < requires.length; i++) { 135 r = requires[i]; 136 rMod = mods[r]; 137 if (rMod && rMod.status === ATTACHED) { 138 //no need 139 } else { 140 attachModByName(self, r, fn); 141 } 142 } 143 144 // load and attach this module 145 loadModByScript(self, mod, function () { 146 147 // KISSY.add 可能改了 config,这里重新取下 148 newRequires = utils.normalizeModNames(SS, mod.requires, mod.name); 149 150 var needToLoad = []; 151 152 //本模块下载成功后串行下载 require 153 for (i = 0; i < newRequires.length; i++) { 154 var r = newRequires[i], 155 rMod = mods[r], 156 inA = S.inArray(r, requires); 157 //已经处理过了或将要处理 158 if (rMod && 159 rMod.status === ATTACHED || 160 //已经正在处理了 161 inA) { 162 //no need 163 } else { 164 //新增的依赖项 165 needToLoad.push(r); 166 } 167 } 168 169 if (needToLoad.length) { 170 for (i = 0; i < needToLoad.length; i++) { 171 attachModByName(self, needToLoad[i], fn); 172 } 173 } else { 174 fn(); 175 } 176 }); 177 178 function fn() { 179 if ( 180 // 前提条件,本模块 script onload 已经调用 181 // ie 下 add 与 script onload 并不连续!! 182 // attach 以 newRequires 为准 183 newRequires && 184 !callbackBeCalled && 185 // 2012-03-16 by yiminghe@gmail.com 186 // add 与 onload ie 下不连续 187 // c 依赖 a 188 // a 模块 add 时进行 attach 189 // a add 后 c 模块 onload 触发 190 // 检测到 a 已经 attach 则调用该函数 191 // a onload 后又调用该函数则需要用 callbackBeCalled 来把门 192 utils.isAttached(SS, newRequires)) { 193 194 utils.attachMod(SS, mod); 195 196 if (mod.status == ATTACHED) { 197 callbackBeCalled = 1; 198 callback(); 199 } 200 } 201 } 202 } 203 204 205 /** 206 * Load a single module. 207 */ 208 function loadModByScript(self, mod, callback) { 209 var SS = self.SS, 210 charset = mod.getCharset(), 211 url = mod.getFullPath(), 212 isCss = utils.isCss(url) 213 214 mod.status = mod.status || INIT; 215 216 if (mod.status < LOADING) { 217 mod.status = LOADING; 218 if (IE && !isCss) { 219 self.__startLoadModuleName = mod.name; 220 self.__startLoadTime = Number(+new Date()); 221 } 222 S.getScript(url, { 223 // syntaxError in all browser will trigger this 224 // same as #111 : https://github.com/kissyteam/kissy/issues/111 225 success:function () { 226 if (!isCss) { 227 // 载入 css 不需要这步了 228 // 标准浏览器下:外部脚本执行后立即触发该脚本的 load 事件,ie9 还是不行 229 if (self[CURRENT_MODULE]) { 230 S.log("standard browser get modname after load : " + mod.name); 231 utils.registerModule(SS, 232 mod.name, self[CURRENT_MODULE].fn, 233 self[CURRENT_MODULE].config); 234 self[CURRENT_MODULE] = null; 235 } 236 } 237 checkAndHandle(); 238 }, 239 error:checkAndHandle, 240 // source:mod.name + "-init", 241 charset:charset 242 }); 243 } 244 // 已经在加载中,需要添加回调到 script onload 中 245 // 注意:没有考虑 error 情形,只在第一次处理即可 246 // 交给 getScript 排队 247 else if (mod.status == LOADING) { 248 S.getScript(url, { 249 success:checkAndHandle, 250 // source:mod.name + "-loading", 251 charset:charset 252 }); 253 } 254 // loaded/attached/error 255 else { 256 checkAndHandle(); 257 } 258 259 function checkAndHandle() { 260 if (isCss || mod.fn) { 261 // css 不会设置 LOADED! 必须外部设置 262 if (isCss && mod.status != ATTACHED) { 263 mod.status = LOADED; 264 } 265 callback(); 266 } else { 267 // ie will call success even when getScript error(404) 268 _modError(); 269 } 270 } 271 272 function _modError() { 273 S.log(mod.name + ' is not loaded! can not find module in path : ' + mod['fullpath'], 'error'); 274 mod.status = ERROR; 275 } 276 } 277 })(KISSY);