UNPKG

zoo-kernel

Version:
1,026 lines (949 loc) 30.4 kB
/* * fis * http://fis.baidu.com/ */ 'use strict'; var fs = require('fs'), pth = require('path'), crypto = require('crypto'), Url = require('url'), _exists = fs.existsSync || pth.existsSync, toString = Object.prototype.toString, iconv, tar; var IS_WIN = process.platform.indexOf('win') === 0; var TEXT_FILE_EXTS = [ 'css', 'tpl', 'js', 'php', 'phtml', 'txt', 'json', 'xml', 'htm', 'text', 'xhtml', 'html', 'md', 'conf', 'po', 'config', 'tmpl', 'coffee', 'less', 'sass', 'jsp', 'scss', 'manifest', 'bak', 'asp', 'tmp', 'haml', 'jade', 'aspx', 'ashx', 'java', 'py', 'c', 'cpp', 'h', 'cshtml', 'asax', 'master', 'ascx', 'cs', 'ftl', 'vm', 'ejs', 'styl', 'jsx', 'handlebars' ], IMAGE_FILE_EXTS = [ 'svg', 'tif', 'tiff', 'wbmp', 'png', 'bmp', 'fax', 'gif', 'ico', 'jfif', 'jpe', 'jpeg', 'jpg', 'woff', 'cur', 'webp', 'swf', 'ttf', 'eot' ], MIME_MAP = { //text 'css' : 'text/css', 'tpl' : 'text/html', 'js' : 'text/javascript', 'jsx' : 'text/javascript', 'php' : 'text/html', 'asp' : 'text/html', 'jsp' : 'text/jsp', 'txt' : 'text/plain', 'json' : 'application/json', 'xml' : 'text/xml', 'htm' : 'text/html', 'text' : 'text/plain', 'md' : 'text/plain', 'xhtml' : 'text/html', 'html' : 'text/html', 'conf' : 'text/plain', 'po' : 'text/plain', 'config' : 'text/plain', 'coffee' : 'text/javascript', 'less' : 'text/css', 'sass' : 'text/css', 'scss' : 'text/css', 'styl' : 'text/css', 'manifest' : 'text/cache-manifest', //image 'svg' : 'image/svg+xml', 'tif' : 'image/tiff', 'tiff' : 'image/tiff', 'wbmp' : 'image/vnd.wap.wbmp', 'webp' : 'image/webp', 'png' : 'image/png', 'bmp' : 'image/bmp', 'fax' : 'image/fax', 'gif' : 'image/gif', 'ico' : 'image/x-icon', 'jfif' : 'image/jpeg', 'jpg' : 'image/jpeg', 'jpe' : 'image/jpeg', 'jpeg' : 'image/jpeg', 'eot' : 'application/vnd.ms-fontobject', 'woff' : 'application/font-woff', 'ttf' : 'application/octet-stream', 'cur' : 'application/octet-stream' }; function getIconv(){ if(!iconv){ iconv = require('iconv-lite'); } return iconv; } function getTar(){ if(!tar){ tar = require('tar'); } return tar; } var _ = module.exports = function(path){ var type = typeof path; if(arguments.length > 1) { path = Array.prototype.join.call(arguments, '/'); } else if(type === 'string') { //do nothing for quickly determining. } else if(type === 'object') { path = Array.prototype.join.call(path, '/'); } else if(type === 'undefined') { path = ''; } if(path){ path = pth.normalize(path.replace(/[\/\\]+/g, '/')).replace(/\\/g, '/'); if(path !== '/'){ path = path.replace(/\/$/, ''); } } return path; }; _.is = function(source, type){ return toString.call(source) === '[object ' + type + ']'; }; _.map = function(obj, callback, merge){ var index = 0; for(var key in obj){ if(obj.hasOwnProperty(key)){ if(merge){ callback[key] = obj[key]; } else if(callback(key, obj[key], index++)) { break; } } } }; _.pad = function(str, len, fill, pre){ if(str.length < len){ fill = (new Array(len)).join(fill || ' '); if(pre){ str = (fill + str).substr(-len); } else { str = (str + fill).substring(0, len); } } return str; }; _.merge = function(source, target){ if(_.is(source, 'Object') && _.is(target, 'Object')){ _.map(target, function(key, value){ source[key] = _.merge(source[key], value); }); } else { source = target; } return source; }; _.clone = function(source) { var ret; switch(toString.call(source)){ case '[object Object]': ret = {}; _.map(source, function(k, v){ ret[k] = _.clone(v); }); break; case '[object Array]': ret = []; source.forEach(function(ele){ ret.push(_.clone(ele)); }); break; default : ret = source; } return ret; }; _.escapeReg = function(str){ return str.replace(/[\.\\\+\*\?\[\^\]\$\(\){}=!<>\|:\/]/g, '\\$&'); }; _.escapeShellCmd = function(str){ return str.replace(/ /g, '"$&"'); }; _.escapeShellArg = function(cmd){ return '"' + cmd + '"'; }; _.stringQuote = function(str, quotes){ var info = { origin : str, rest : str = str.trim(), quote : '' }; if(str){ quotes = quotes || '\'"'; var strLen = str.length - 1; for(var i = 0, len = quotes.length; i < len; i++){ var c = quotes[i]; if(str[0] === c && str[strLen] === c){ info.quote = c; info.rest = str.substring(1, strLen); break; } } } return info; }; _.getMimeType = function(ext){ if(ext[0] === '.'){ ext = ext.substring(1); } return MIME_MAP[ext] || 'application/x-' + ext; }; _.exists = _exists; _.fs = fs; _.realpath = function(path){ if(path && _exists(path)){ path = fs.realpathSync(path); if(IS_WIN){ path = path.replace(/\\/g, '/'); } if(path !== '/'){ path = path.replace(/\/$/, ''); } return path; } else { return false; } }; _.realpathSafe = function(path){ return _.realpath(path) || _(path); }; _.isAbsolute = function(path) { if (IS_WIN) { return /^[a-z]:/i.test(path); } else { if(path === '/'){ return true; } else { var split = path.split('/'); if(split[0] === '~'){ return true; } else if(split[0] === '' && split[1]) { return _.isDir('/' + split[1] + '/' + split[2]); } else { return false; } } } }; _.isFile = function(path){ return _exists(path) && fs.statSync(path).isFile(); }; _.isDir = function(path){ return _exists(path) && fs.statSync(path).isDirectory(); }; _.mtime = function(path){ var time = 0; if(_exists(path)){ time = fs.statSync(path).mtime; } return time; }; _.touch = function(path, mtime){ if(!_exists(path)){ _.write(path, ''); } if(mtime instanceof Date){ //do nothing for quickly determining. } else if(typeof mtime === 'number') { var time = new Date(); time.setTime(mtime); mtime = time; } else { fis.log.error('invalid argument [mtime]'); } fs.utimesSync(path, mtime, mtime); }; _.isWin = function(){ return IS_WIN; }; function getFileTypeReg(type){ var map = [], ext = fis.config.get('project.fileType.' + type); if(type === 'text'){ map = TEXT_FILE_EXTS; } else if(type === 'image'){ map = IMAGE_FILE_EXTS; } else { fis.log.error('invalid file type [' + type + ']'); } if(ext && ext.length){ if(typeof ext === 'string'){ ext = ext.split(/\s*,\s*/); } map = map.concat(ext); } map = map.join('|'); return new RegExp('\\.(?:' + map + ')$', 'i'); } _.isTextFile = function(path){ return getFileTypeReg('text').test(path || ''); }; _.isImageFile = function(path){ return getFileTypeReg('image').test(path || ''); }; _.md5 = function(data, len){ var md5sum = crypto.createHash('md5'), encoding = typeof data === 'string' ? 'utf8' : 'binary'; md5sum.update(data, encoding); len = len || fis.config.get('project.md5Length', 7); return md5sum.digest('hex').substring(0, len); }; _.base64 = function(data){ if(data instanceof Buffer){ //do nothing for quickly determining. } else if(data instanceof Array){ data = new Buffer(data); } else { //convert to string. data = new Buffer(String(data || '')); } return data.toString('base64'); }; _.mkdir = function(path, mode){ if (typeof mode === 'undefined') { //511 === 0777 mode = 511 & (~process.umask()); } if(_exists(path)) return; path.split('/').reduce(function(prev, next) { if(prev && !_exists(prev)) { fs.mkdirSync(prev, mode); } return prev + '/' + next; }); if(!_exists(path)) { fs.mkdirSync(path, mode); } }; _.toEncoding = function(str, encoding){ return getIconv().toEncoding(String(str), encoding); }; _.isUtf8 = function(bytes) { var i = 0; while(i < bytes.length) { if((// ASCII 0x00 <= bytes[i] && bytes[i] <= 0x7F )) { i += 1; continue; } if((// non-overlong 2-byte (0xC2 <= bytes[i] && bytes[i] <= 0xDF) && (0x80 <= bytes[i+1] && bytes[i+1] <= 0xBF) )) { i += 2; continue; } if( (// excluding overlongs bytes[i] == 0xE0 && (0xA0 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) && (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) ) || (// straight 3-byte ((0xE1 <= bytes[i] && bytes[i] <= 0xEC) || bytes[i] == 0xEE || bytes[i] == 0xEF) && (0x80 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) && (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) ) || (// excluding surrogates bytes[i] == 0xED && (0x80 <= bytes[i + 1] && bytes[i + 1] <= 0x9F) && (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) ) ) { i += 3; continue; } if( (// planes 1-3 bytes[i] == 0xF0 && (0x90 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) && (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) && (0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF) ) || (// planes 4-15 (0xF1 <= bytes[i] && bytes[i] <= 0xF3) && (0x80 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) && (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) && (0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF) ) || (// plane 16 bytes[i] == 0xF4 && (0x80 <= bytes[i + 1] && bytes[i + 1] <= 0x8F) && (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) && (0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF) ) ) { i += 4; continue; } return false; } return true; }; _.readBuffer = function(buffer){ if(_.isUtf8(buffer)){ buffer = buffer.toString('utf8'); if (buffer.charCodeAt(0) === 0xFEFF) { buffer = buffer.substring(1); } } else { buffer = getIconv().decode(buffer, 'gbk'); } return buffer; }; _.read = function(path, convert){ var content = false; if(_exists(path)){ content = fs.readFileSync(path); if(convert || _.isTextFile(path)){ content = _.readBuffer(content); } } else { fis.log.error('unable to read file[' + path + ']: No such file or directory.'); } return content; }; _.write = function(path, data, charset, append){ if(!_exists(path)){ _.mkdir(_.pathinfo(path).dirname); } if(charset){ data = getIconv().encode(data, charset); } if(append) { fs.appendFileSync(path, data, null); } else { fs.writeFileSync(path, data, null); } }; _.filter = function(str, include, exclude){ function normalize(pattern){ var type = toString.call(pattern); switch (type){ case '[object String]': return _.glob(pattern); case '[object RegExp]': return pattern; default: fis.log.error('invalid regexp [' + pattern + '].'); } } function match(str, patterns){ var matched = false; if (!_.is(patterns, 'Array')){ patterns = [patterns]; } patterns.every(function(pattern){ if (!pattern){ return true; } matched = matched || str.search(normalize(pattern)) > -1; return !matched; }); return matched; } var isInclude, isExclude; if (include) { isInclude = match(str, include); }else{ isInclude = true; } if (exclude) { isExclude = match(str, exclude); } return isInclude && !isExclude; }; _.find = function(rPath, include, exclude){ var list = [], path = _.realpath(rPath); if(path){ var stat = fs.statSync(path); if(stat.isDirectory()){ fs.readdirSync(path).forEach(function(p){ if(p[0] != '.') { list = list.concat(_.find(path + '/' + p, include, exclude)); } }); } else if(stat.isFile() && _.filter(path, include, exclude)) { list.push(path); } } else { fis.log.error('unable to find [' + rPath + ']: No such file or directory.'); } return list.sort(); }; _.del = function(rPath, include, exclude){ var removedAll = true; var path; if(rPath && _.exists(rPath)) { var stat = fs.lstatSync(rPath); var isFile = stat.isFile() || stat.isSymbolicLink(); if (stat.isSymbolicLink()) { path = rPath; } else { path = _.realpath(rPath); } if(/^(?:\w:)?\/$/.test(path)){ fis.log.error('unable to delete directory [' + rPath + '].'); } if(stat.isDirectory()){ fs.readdirSync(path).forEach(function(name){ if(name != '.' && name != '..') { removedAll = _.del(path + '/' + name, include, exclude) && removedAll; } }); if(removedAll) { fs.rmdirSync(path); } } else if(isFile && _.filter(path, include, exclude)) { fs.unlinkSync(path); } else { removedAll = false; } } else { //fis.log.error('unable to delete [' + rPath + ']: No such file or directory.'); } return removedAll; }; _.copy = function(rSource, target, include, exclude, uncover, move){ var removedAll = true, source = _.realpath(rSource); target = _(target); if(source){ var stat = fs.statSync(source); if(stat.isDirectory()){ fs.readdirSync(source).forEach(function(name){ if(name != '.' && name != '..') { removedAll = _.copy( source + '/' + name, target + '/' + name, include, exclude, uncover, move ) && removedAll; } }); if(move && removedAll) { fs.rmdirSync(source); } } else if(stat.isFile() && _.filter(source, include, exclude)){ if(uncover && _exists(target)){ //uncover removedAll = false; } else { _.write(target, fs.readFileSync(source, null)); if(move) { fs.unlinkSync(source); } } } else { removedAll = false; } } else { fis.log.error('unable to copy [' + rSource + ']: No such file or directory.'); } return removedAll; }; _.ext = function(str){ var info = _.query(str), pos; str = info.fullname = info.rest; if((pos = str.lastIndexOf('/')) > -1){ if(pos === 0){ info.rest = info.dirname = '/'; } else { info.dirname = str.substring(0, pos); info.rest = info.dirname + '/'; } str = str.substring(pos + 1); } else { info.rest = info.dirname = ''; } if((pos = str.lastIndexOf('.')) > -1){ info.ext = str.substring(pos).toLowerCase(); info.filename = str.substring(0, pos); info.basename = info.filename + info.ext; } else { info.basename = info.filename = str; info.ext = ''; } info.rest += info.filename; return info; }; _.query = function(str){ var rest = str, pos = rest.indexOf('#'), hash = '', query = ''; if(pos > -1){ hash = rest.substring(pos); rest = rest.substring(0, pos); } pos = rest.indexOf('?'); if(pos > -1){ query = rest.substring(pos); rest = rest.substring(0, pos); } rest = rest.replace(/\\/g, '/'); if(rest !== '/'){ rest = rest.replace(/\/\.?$/, ''); } return { origin : str, rest : rest, hash : hash, query : query }; }; _.pathinfo = function(path){ //can not use _() method directly for the case _.pathinfo('a/'). var type = typeof path; if(arguments.length > 1) { path = Array.prototype.join.call(arguments, '/'); } else if(type === 'string') { //do nothing for quickly determining. } else if(type === 'object') { path = Array.prototype.join.call(path, '/'); } return _.ext(path); }; _.camelcase = function(str){ var ret = ''; if(str){ str.split(/[-_ ]+/).forEach(function(ele){ ret += ele[0].toUpperCase() + ele.substring(1); }); } else { ret = str; } return ret; }; _.pipe = function(type, callback, def){ var processors = fis.config.get('modules.' + type, def); if(processors){ var typeOf = typeof processors; if(typeOf === 'string'){ processors = processors.trim().split(/\s*,\s*/); } else if(typeOf === 'function'){ processors = [ processors ]; } type = type.split('.')[0]; processors.forEach(function(processor, index){ var typeOf = typeof processor, key; if(typeOf === 'string'){ key = type + '.' + processor; processor = fis.require(type, processor); } else { key = type + '.' + index; } if(typeof processor === 'function'){ var settings = fis.config.get('settings.' + key, {}); if(processor.defaultOptions){ settings = _.merge(processor.defaultOptions, settings); } callback(processor, settings, key); } else { fis.log.warning('invalid processor [modules.' + key + ']'); } }); } }; _.parseUrl = function(url, opt){ opt = opt || {}; url = Url.parse(url); var ssl = url.protocol === 'https:'; opt.host = opt.host || opt.hostname || ((ssl || url.protocol === 'http:') ? url.hostname : 'localhost'); opt.port = opt.port || (url.port || (ssl ? 443 : 80)); opt.path = opt.path || (url.pathname + (url.search ? url.search : '')); opt.method = opt.method || 'GET'; opt.agent = opt.agent || false; return opt; }; _.download = function(url, callback, extract, opt){ opt = _.parseUrl(url, opt || {}); var http = opt.protocol === 'https:' ? require('https') : require('http'), name = _.md5(url, 8) + _.ext(url).ext, tmp = fis.project.getTempPath('downloads', name), data = opt.data; delete opt.data; _.write(tmp, ''); var writer = fs.createWriteStream(tmp), http_err_handler = function(err){ writer.destroy(); fs.unlinkSync(tmp); var msg = typeof err === 'object' ? err.message : err; if(callback){ callback(msg); } else { fis.log.error('request error [' + msg + ']'); } }, req = http.request(opt, function(res){ var status = res.statusCode; res .on('data', function(chunk){ writer.write(chunk); }) .on('end', function(){ if(status >= 200 && status < 300 || status === 304){ if(extract){ fs .createReadStream(tmp) .pipe(getTar().Extract({ path : extract })) .on('error', function(err){ if(callback){ callback(err); } else { fis.log.error('extract tar file [' + tmp + '] fail, error [' + err + ']'); } }) .on('end', function(){ if(callback && (typeof callback(null, tmp, res) === 'undefined')){ fs.unlinkSync(tmp); } }); } else if(callback && (typeof callback(null, tmp, res) === 'undefined')){ fs.unlinkSync(tmp); } } else { http_err_handler(status); } }) .on('error', http_err_handler); }); req.on('error', http_err_handler); if(data){ req.write(data); } req.end(); }; _.upload = function(url, opt, data, content, subpath, callback){ if(typeof content === 'string'){ content = new Buffer(content, 'utf8'); } else if(!(content instanceof Buffer)){ fis.log.error('unable to upload content [' + (typeof content) + ']'); } data = data || {}; var endl = '\r\n'; var boundary = '-----np' + Math.random(); var collect = []; _.map(data, function(key, value){ collect.push('--' + boundary + endl); collect.push('Content-Disposition: form-data; name="' + key + '"' + endl); collect.push(endl); collect.push(value + endl); }); collect.push('--' + boundary + endl); collect.push('Content-Disposition: form-data; name="file"; filename="' + subpath + '"' + endl); collect.push(endl); collect.push(content); collect.push('--' + boundary + '--' + endl); var length = 0; collect.forEach(function(ele){ length += ele.length; }); opt = opt || {}; opt.method = opt.method || 'POST'; opt.headers = { 'Content-Type': 'multipart/form-data; boundary=' + boundary, 'Content-Length': length }; opt = _.parseUrl(url, opt); var http = opt.protocol === 'https:' ? require('https') : require('http'); var req = http.request(opt, function(res){ var status = res.statusCode; var body = ''; res .on('data', function(chunk){ body += chunk; }) .on('end', function(){ if(status >= 200 && status < 300 || status === 304){ callback(null, body); } else { callback(status); } }) .on('error', function(err){ callback(err.message || err); }); }); collect.forEach(function(d){ req.write(d); if(d instanceof Buffer){ req.write(endl); } }); req.end(); }; _.install = function (name, version, opt){ version = version === '*' ? 'latest' : ( version || 'latest' ); var remote = opt.remote.replace(/^\/$/, ''); var url = remote + '/' + name + '/' + version + '.tar'; var extract = opt.extract || process.cwd(); if(opt.before){ opt.before(name, version); } _.download(url, function(err){ if(err){ if(opt.error){ opt.error(err); } else { fis.log.error( 'unable to download component [' + name + '@' + version + '] from [' + url + '], error [' + err + ']'); } } else { if(opt.done){ opt.done(name, version); } process.stdout.write('install [' + name + '@' + version + ']\n'); var pkg = _(extract, 'package.json'); if(_.isFile(pkg)){ var info = _.readJSON(pkg); fs.unlinkSync(pkg); _.map(info.dependencies || {}, function(depName, depVersion){ _.install(depName, depVersion, opt); }); } } }, extract); }; _.readJSON = function(path){ var json = _.read(path), result = {}; try { result = JSON.parse(json); } catch(e){ fis.log.error('parse json file[' + path + '] fail, error [' + e.message + ']'); } return result; }; _.glob = function(pattern, str){ var sep = _.escapeReg('/'); pattern = new RegExp('^' + sep + '?' + _.escapeReg( pattern .replace(/\\/g, '/') .replace(/^\//, '') ) .replace(new RegExp(sep + '\\*\\*' + sep, 'g'), sep + '.*(?:' + sep + ')?') .replace(new RegExp(sep + '\\*\\*', 'g'), sep + '.*') .replace(/\\\*\\\*/g, '.*') .replace(/\\\*/g, '[^' + sep + ']*') .replace(/\\\?/g, '[^' + sep + ']') + '$', 'i' ); if(typeof str === 'string'){ return pattern.test(str); } else { return pattern; } }; _.nohup = function(cmd, options, callback){ if(typeof options === 'function'){ callback = options; options = null; } var exec = require('child_process').exec; if(IS_WIN){ var cmdEscape = cmd.replace(/"/g, '""'), file = fis.project.getTempPath('nohup-' + _.md5(cmd) + '.vbs'), script = ''; script += 'Dim shell\n'; script += 'Set shell = Wscript.CreateObject("WScript.Shell")\n'; script += 'ret = shell.Run("cmd.exe /c start /b ' + cmdEscape + '", 0, TRUE)\n'; script += 'WScript.StdOut.Write(ret)\n'; script += 'Set shell = NoThing'; _.write(file, script); return exec('cscript.exe /nologo "' + file + '"', options, function(error, stdout){ if(stdout != '0'){ fis.log.error('exec command[' + cmd + '] fail.'); } fs.unlinkSync(file); if(typeof callback === 'function') { callback(); } }); } else { return exec('nohup ' + cmd + ' > /dev/null 2>&1 &', options, function(error, stdout){ if(error !== null) { fis.log.error('exec command[' + cmd + '] fail, stdout [' + stdout + '].'); } if(typeof callback === 'function') { callback(); } }); } }; /** * 获取相对路径 * @param path 资源路径 /a/b/c/d.js * @param refPath 参照路径 /a/b/e/j/h/f.html * @return relativePath 资源路径相对于参照路径的相对路径 ..(j)/..(e)/..(b)/c/d.js */ _.getRelativePath = function(path, refPath){ var dir , refDir, filename = '', dirReg = /[^?#]*\//; function path2dir(path){ var dir = String(path).trim().match(dirReg)[0], temp = dir, len = dir.length, arr; if(dir.charCodeAt(len-1) === 47){ dir = dir.substring(0, len-1); } if(dir.charCodeAt(0) === 47){ dir = dir.substring(1); } //当这个路径直接是a.html时,dir='',而dir.split('/')=[''],它仍然有1个长度,会影响后面相对路径的转换判断 //之前没有触发这个bug是因为,我们的项目里面都给资源添加了一个项目目录... arr = dir ? dir.split('/') : []; return {str: temp, arr: arr}; } function relativePath(arr, refArr){ var len = arr.length, refLen = refArr.length, path = '', num = 0, last = 0; for(var i =0, ll = Math.min(len,refLen); i < ll; i++){ if(arr[i] !== refArr[i]){ break; } num++; } last = refLen - num; while(last > 0){ path += '../'; last--; } return path + (arr.slice(num).length ? arr.slice(num).join('/')+'/' : ''); } dir = path2dir(path); refDir = path2dir(refPath); if(path.length > dir.str.length){ filename = path.substring(Math.max(path.indexOf(dir.str) + dir.str.length, 0)); } return relativePath(dir.arr, refDir.arr) + filename; }; /** * 获取绝对路径 * @param path 相对资源路径 ../../../c/d.js * @param refPath 参照路径 /a/b/e/j/h/f.html * @return absolutePath 资源路径相对于参照路径的相对路径 /a/b/c/d.js */ _.getAbsolutePath = function(path, refPath){ var number, //相对资源路径中../的个数 refDir, //参照路径的目录 filename = '', //资源路径除去../剩下的,总称为filename dirReg = /[^?#]*\//, //目录正则 pReg = /\.\.\//g; path = String(path).trim(); refPath = String(refPath).trim(); number = path.match(pReg) && path.match(pReg).length || 0; filename = path.charCodeAt(0) === 47 && path.substring(1).replace(pReg, '') || path.replace(pReg, ''); refDir = refPath.match(dirReg)[0]; return refDir.split('/').slice(0, number ? -number-1 : -1).join('/') + '/' + filename; }; _.normalize = _;