取得URL相对于另一个URL的相对路径
今天群里灰大出了个题:
1 function getRelativeURL(url, baseURL) {
2 // 获取url相对baseURL的相对路径
3 // url和baseURL都是绝对路径或都是相对路径的情况下,有可能计算出结果,否则扔Error
4 // 如果都是绝对路径,但不在同一个域、同一个协议、同一个端口下,即无法计算相对路径,扔Error
5 }
用了点时间写了一下:
1 function getRelativeURL ( url, baseUrl ) {
2
3 var path,
4 basePath,
5 i = 0,
6 compare = {},
7 r = { 'proto' : /(^[a-zA-Z]{1,4}):\/\/(.*)/,
8 'domain' : /(^[-_a-zA-Z.]+)((?:\/|:|$)?.*)/,
9 'port' : /(?:^:)(\d+)((?:\/|$).*)/,
10 'word' : /[^, ]+/g,
11 };
12
13 compare[ 'url' ] = parseUrl( url );
14 compare[ 'baseUrl' ] = parseUrl( baseUrl );
15
16 if ( different( 'proto' ) || different( 'domain' ) || different( 'port' ) ) return false; //协议、域名、端口不同
17
18 path = compare.url.path;
19 basePath = compare.baseUrl.path;
20
21 while ( path.length && path[ 0 ] == basePath[ 0 ] ) { //去除相同父目录
22 path.shift();
23 basePath.shift();
24 }
25
26 if ( path.length == 0 && basePath.length == 0 ) return './'; //两个url相同
27
28 if ( path.length == 0 ) return parent( basePath.length ); //baseUrl是url的子目录
29
30 if ( basePath.length == 0 ) return path.join( '/' ) + '/'; //url是baseUrl的子目录
31
32 return parent( basePath.length ) + path.join( '/' ) + '/'; //url和baseUrl互不包含
33
34 function different ( name ) {
35 return compare[ 'url' ][ name ] != compare[ 'baseUrl' ][ name ];
36 }
37
38 function notValidUrl () {
39 return compare[ 'url' ][ 'proto' ] == '' || compare[ 'baseUrl' ][ 'proto' ] == '';
40 }
41
42 function parseUrl ( url ) {
43 var parsed = {};
44 'proto,domain,port'.replace( r.word, function( name ){
45 var match, reg = r[ name ];
46 if ( !reg.test( url ) ) {
47 parsed[ name ] = '';
48 return;
49 }
50 match = url.match( reg );
51 url = match[ 2 ];
52 parsed[ name ] = match[ 1 ];
53 });
54 parsed[ 'path' ] = url.replace(/\/+$/,'').split('/');
55 return parsed;
56 }
57
58 function parent ( level ) {
59 console.log(level);
60 var result = [];
61 while ( level-- ) result.push( '../' );
62 return result.join('');
63 }
64 };
使用单元测试如下:
1 test("url和baseURL并非都是绝对路径或都是相对路径,没有结果", function() {
2 expect(4);
3 ok(!getRelativeURL('http://aoeu/ao','/aoeu'),'绝对,相对');
4 ok(!getRelativeURL('/aoeu','http://aoeu/ao'),'相对,绝对');
5 ok(getRelativeURL('http://aoeu','http://aoeu/ao'),'绝对,绝对');
6 ok(getRelativeURL('/aoeu','/ao'),'相对,相对');
7 });
8
9 test("不在同一个域、同一个协议、同一个端口下,没有结果", function() {
10 expect(3);
11 ok(!getRelativeURL('http://aoc.com','http://aoeu.com'),'不同域');
12 ok(!getRelativeURL('https://aoc.com','ftp://aoeu.com'),'不同协议');
13 ok(!getRelativeURL('https://aoc.com:88','ftp://aoc.com:89'),'不同端口');
14 });
15
16 test("是否以/结尾不影响结果",function() {
17 expect(3);
18 equal(getRelativeURL('http://aoeu.com/aoeu/','http://aoeu.com/'),getRelativeURL('http://aoeu.com/aoeu','http://aoeu.com'),'都以/结尾');
19 equal(getRelativeURL('http://aoeu.com/aoeu/','http://aoeu.com'),getRelativeURL('http://aoeu.com/aoeu','http://aoeu.com'),'url以/结尾');
20 equal(getRelativeURL('http://aoeu.com/aoeu','http://aoeu.com/'),getRelativeURL('http://aoeu.com/aoeu','http://aoeu.com'),'baseUrl以/结尾');
21 })
22
23 test("相同目录",function(){
24 expect(3);
25 equal(getRelativeURL('http://aoeu.com','http://aoeu.com/'),'./','根目录');
26 equal(getRelativeURL('http://aoeu.com/aoeu','http://aoeu.com/aoeu'),'./','单级');
27 equal(getRelativeURL('http://aoeu.com/ao/eu','http://aoeu.com/ao/eu/'),'./','多级');
28 })
29
30 test("url和baseUrl中有一个为根目录",function() {
31 expect(4);
32 equal(getRelativeURL('http://aoeu.com/aoeu','http://aoeu.com'),'aoeu/','url为baseUrl的单级子目录');
33 equal(getRelativeURL('http://aoeu.com/ao/eu','http://aoeu.com'),'ao/eu/','url为baseUrl的多级子目录');
34 equal(getRelativeURL('http://aoeu.com','http://aoeu.com/ao'),'../','baseUrl为url的单级子目录');
35equal(getRelativeURL('http://aoeu.com','http://aoeu.com/ao/eu'),'../ ../','baseUrl为url的多级子目录'); //那啥,不是我多写一个空格,博客园blog会把正确的写法换成自己的url,很奇怪
!
36 })
37
38 test("其他",function() {
39 expect(2);
40 equal(getRelativeURL('http://aoeu.com/aoeu','http://aoeu.com/ueoa'),'../aoeu/','单级');
41 equal(getRelativeURL('http://aoeu.com/ao/eu','http://aoeu.com/eu/ao'),'../ ../ao/eu/','多级'); //理由同上
42 })
如有更好解法或发现错误,请不吝赐教:)