1 /** 2 * @fileOverview utils for kissy loader 3 * @author yiminghe@gmail.com 4 */ 5 (function (S, undefined) { 6 7 if (typeof require !== 'undefined') { 8 return; 9 } 10 11 var Loader = S.Loader, 12 ua = navigator.userAgent, 13 startsWith = S.startsWith, 14 data = Loader.STATUS, 15 utils = {}, 16 host = S.Env.host, 17 win = host, 18 doc = host.document, 19 loc = host.location, 20 // 当前页面所在的目录 21 // http://xx.com/y/z.htm#!/f/g 22 // -> 23 // http://xx.com/y/ 24 __pagePath = loc.href.replace(loc.hash, "").replace(/[^/]*$/i, ""); 25 26 // http://wiki.commonjs.org/wiki/Packages/Mappings/A 27 // 如果模块名以 / 结尾,自动加 index 28 function indexMap(s) { 29 if (S.isArray(s)) { 30 var ret = []; 31 S.each(s, function (si) { 32 ret.push(indexMap(si)); 33 }); 34 return ret; 35 } 36 return indexMapStr(s); 37 } 38 39 function indexMapStr(s) { 40 if (/(.+\/)(\?t=.+)?$/.test(s)) { 41 return RegExp.$1 + "index" + RegExp.$2; 42 } else { 43 return s 44 } 45 } 46 47 48 function getPackageInfo(self, mod) { 49 50 var modName = mod.name, 51 Env = self.Env, 52 packages = Env.packages || {}, 53 pName = "", 54 packageDesc; 55 56 for (var p in packages) { 57 if (packages.hasOwnProperty(p)) { 58 if (S.startsWith(modName, p) && 59 p.length > pName.length) { 60 pName = p; 61 } 62 } 63 } 64 65 packageDesc = packages[pName] || 66 Env.defaultPackage || 67 (Env.defaultPackage = new Loader.Package({SS:self})); 68 69 mod.packageInfo = packageDesc; 70 71 return packageDesc; 72 } 73 74 75 var isWebKit = !!ua.match(/AppleWebKit/); 76 77 S.mix(utils, { 78 79 docHead:function () { 80 return doc.getElementsByTagName('head')[0] || doc.documentElement; 81 }, 82 83 isWebKit:isWebKit, 84 85 // like Gecko ... 86 isGecko:!isWebKit && !!ua.match(/Gecko/), 87 88 isPresto:!!ua.match(/Presto/), 89 90 IE:!!ua.match(/MSIE/), 91 92 isCss:function (url) { 93 return /\.css(?:\?|$)/i.test(url); 94 }, 95 96 /** 97 * resolve relative part of path 98 * x/../y/z -> y/z 99 * x/./y/z -> x/y/z 100 * @param path uri path 101 * @return {string} resolved path 102 * @description similar to path.normalize in nodejs 103 */ 104 normalizePath:function (path) { 105 var paths = path.split("/"), 106 re = [], 107 p; 108 for (var i = 0; i < paths.length; i++) { 109 p = paths[i]; 110 if (p == ".") { 111 } else if (p == "..") { 112 re.pop(); 113 } else { 114 re.push(p); 115 } 116 } 117 return re.join("/"); 118 }, 119 120 /** 121 * 根据当前模块以及依赖模块的相对路径,得到依赖模块的绝对路径 122 * @param moduleName 当前模块 123 * @param depName 依赖模块 124 * @return {string|Array} 依赖模块的绝对路径 125 * @description similar to path.resolve in nodejs 126 */ 127 normalDepModuleName:function (moduleName, depName) { 128 if (!depName) { 129 return depName; 130 } 131 if (S.isArray(depName)) { 132 for (var i = 0; i < depName.length; i++) { 133 depName[i] = utils.normalDepModuleName(moduleName, depName[i]); 134 } 135 return depName; 136 } 137 if (startsWith(depName, "../") || startsWith(depName, "./")) { 138 var anchor = "", index; 139 // x/y/z -> x/y/ 140 if ((index = moduleName.lastIndexOf("/")) != -1) { 141 anchor = moduleName.substring(0, index + 1); 142 } 143 return normalizePath(anchor + depName); 144 } else if (depName.indexOf("./") != -1 145 || depName.indexOf("../") != -1) { 146 return normalizePath(depName); 147 } else { 148 return depName; 149 } 150 }, 151 152 //去除后缀名,要考虑时间戳! 153 removePostfix:function (path) { 154 return path.replace(/(-min)?\.js[^/]*$/i, ""); 155 }, 156 157 /** 158 * 路径正则化,不能是相对地址 159 * 相对地址则转换成相对页面的绝对地址 160 * 用途: 161 * package path 相对地址则相对于当前页面获取绝对地址 162 */ 163 normalBasePath:function (path) { 164 path = S.trim(path); 165 166 // path 为空时,不能变成 "/" 167 if (path && 168 path.charAt(path.length - 1) != '/') { 169 path += "/"; 170 } 171 172 /** 173 * 一定要正则化,防止出现 ../ 等相对路径 174 * 考虑本地路径 175 */ 176 if (!path.match(/^(http(s)?)|(file):/i) && 177 !startsWith(path, "/")) { 178 path = __pagePath + path; 179 } 180 181 if (startsWith(path, "/")) { 182 var loc = win.location; 183 path = loc.protocol + "//" + loc.host + path; 184 } 185 186 return normalizePath(path); 187 }, 188 189 /** 190 * 相对路径文件名转换为绝对路径 191 * @param path 192 */ 193 absoluteFilePath:function (path) { 194 path = utils.normalBasePath(path); 195 return path.substring(0, path.length - 1); 196 }, 197 198 createModulesInfo:function (self, modNames) { 199 S.each(modNames, function (m) { 200 utils.createModuleInfo(self, m); 201 }); 202 }, 203 204 createModuleInfo:function (self, modName, cfg) { 205 var mods = self.Env.mods, 206 t, 207 mod = mods[modName]; 208 209 if (mod) { 210 return mod; 211 } 212 213 // 防止 cfg 里有 tag,构建 fullpath 需要 214 mods[modName] = mod = new Loader.Module(S.mix({ 215 name:modName, 216 SS:self 217 }, cfg)); 218 219 var packageInfo = getPackageInfo(self, mod), 220 path = defaultComponentJsName(modName, packageInfo); 221 222 // 用户配置的 path优先 223 S.mix(mod, { 224 path:path, 225 packageInfo:packageInfo 226 }, false); 227 228 return mod; 229 }, 230 231 isAttached:function (self, modNames) { 232 return isStatus(self, modNames, data.ATTACHED); 233 }, 234 235 isLoaded:function (self, modNames) { 236 return isStatus(self, modNames, data.LOADED); 237 }, 238 239 getModules:function (self, modNames) { 240 var mods = [self]; 241 242 S.each(modNames, function (modName) { 243 if (!utils.isCss(modName)) { 244 mods.push(self.require(modName)); 245 } 246 }); 247 248 return mods; 249 }, 250 251 attachMod:function (self, mod) { 252 253 if (mod.status != data.LOADED) { 254 return; 255 } 256 257 var fn = mod.fn, 258 requires, 259 value; 260 261 // 需要解开 index,相对路径,去除 tag,但是需要保留 alias,防止值不对应 262 requires = mod.requires = utils.normalizeModNamesWithAlias(self, mod.requires, mod.name); 263 264 if (fn) { 265 if (S.isFunction(fn)) { 266 // context is mod info 267 value = fn.apply(mod, utils.getModules(self, requires)); 268 } else { 269 value = fn; 270 } 271 mod.value = value; 272 } 273 274 mod.status = data.ATTACHED; 275 276 self.getLoader().fire("afterModAttached", { 277 mod:mod 278 }); 279 }, 280 281 getModNamesAsArray:function (modNames) { 282 if (S.isString(modNames)) { 283 modNames = modNames.replace(/\s+/g, "").split(','); 284 } 285 return modNames; 286 }, 287 288 289 indexMapStr:indexMapStr, 290 291 /** 292 * Three effects: 293 * 1. add index : / => /index 294 * 2. unalias : core => dom,event,ua 295 * 3. relative to absolute : ./x => y/x 296 * @param {KISSY} self Global KISSY instance 297 * @param {String|String[]} modNames Array of module names or module names string separated by comma 298 */ 299 normalizeModNames:function (self, modNames, refModName) { 300 return utils.unalias(self, utils.normalizeModNamesWithAlias(self, modNames, refModName)); 301 }, 302 303 unalias:function (self, names) { 304 var ret = [].concat(names), 305 i, 306 m, 307 alias, 308 ok = 0, 309 mods = self['Env'].mods; 310 while (!ok) { 311 ok = 1; 312 for (i = ret.length - 1; i >= 0; i--) { 313 if ((m = mods[ret[i]]) && (alias = m.alias)) { 314 ok = 0; 315 ret.splice.apply(ret, [i, 1].concat(indexMap(alias))); 316 } 317 } 318 } 319 return ret; 320 }, 321 322 normalizeModNamesWithAlias:function (self, modNames, refModName) { 323 var ret = [], i, l; 324 if (modNames) { 325 // 1. index map 326 for (i = 0, l = modNames.length; i < l; i++) { 327 ret.push(indexMap(modNames[i])); 328 } 329 } 330 // 3. relative to absolute (optional) 331 if (refModName) { 332 ret = utils.normalDepModuleName(refModName, ret); 333 } 334 return ret; 335 }, 336 337 // 注册模块,将模块和定义 factory 关联起来 338 registerModule:function (self, name, fn, config) { 339 var mods = self.Env.mods, 340 mod = mods[name]; 341 342 if (mod && mod.fn) { 343 S.log(name + " is defined more than once"); 344 return; 345 } 346 347 // 没有 use,静态载入的 add 可能执行 348 utils.createModuleInfo(self, name); 349 350 mod = mods[name]; 351 352 // 注意:通过 S.add(name[, fn[, config]]) 注册的代码,无论是页面中的代码, 353 // 还是 js 文件里的代码,add 执行时,都意味着该模块已经 LOADED 354 S.mix(mod, { name:name, status:data.LOADED }); 355 356 357 mod.fn = fn; 358 359 S.mix((mods[name] = mod), config); 360 361 S.log(name + " is loaded"); 362 }, 363 364 getMappedPath:function (self, path) { 365 var __mappedRules = self.Config.mappedRules || []; 366 for (var i = 0; i < __mappedRules.length; i++) { 367 var m, rule = __mappedRules[i]; 368 if (m = path.match(rule[0])) { 369 return path.replace(rule[0], rule[1]); 370 } 371 } 372 return path; 373 }, 374 375 /** 376 * test3,test3/a/b => a/b 377 */ 378 removePackageNameFromModName:function () { 379 var cache = {}; 380 return function (packageName, modName) { 381 if (!packageName) { 382 return modName; 383 } 384 if (!S.endsWith(packageName, "/")) { 385 packageName += "/"; 386 } 387 var reg; 388 if (!(reg = cache[packageName])) { 389 reg = cache[packageName] = new RegExp("^" + S.escapeRegExp(packageName)); 390 } 391 return modName.replace(reg, ""); 392 } 393 }() 394 395 }); 396 397 function defaultComponentJsName(m, packageInfo) { 398 var suffix = ".js", 399 match; 400 if (match = m.match(/(.+)(\.css)$/i)) { 401 suffix = match[2]; 402 m = match[1]; 403 } 404 var min = "-min"; 405 if (packageInfo.isDebug()) { 406 min = ""; 407 } 408 return m + min + suffix; 409 } 410 411 function isStatus(self, modNames, status) { 412 var mods = self.Env.mods, 413 i; 414 modNames = S.makeArray(modNames); 415 for (i = 0; i < modNames.length; i++) { 416 var mod = mods[modNames[i]]; 417 if (!mod || mod.status !== status) { 418 return false; 419 } 420 } 421 return true; 422 } 423 424 var normalizePath = utils.normalizePath; 425 426 Loader.Utils = utils; 427 428 })(KISSY);