取得URL相对于另一个URL的相对路径

今天群里灰大出了个题:

function getRelativeURL(url, baseURL) {
   // 获取url相对baseURL的相对路径
   // url和baseURL都是绝对路径或都是相对路径的情况下,有可能计算出结果,否则扔Error
   // 如果都是绝对路径,但不在同一个域、同一个协议、同一个端口下,即无法计算相对路径,扔Error
}

用了点时间写了一下:

function getRelativeURL ( url, baseUrl ) {

    var path, 
        basePath,
        i = 0,
        compare = {},
        r = {    'proto' : /(^[a-zA-Z]{1,4}):\/\/(.*)/,
                'domain' : /(^[-_a-zA-Z.]+)((?:\/|:|$)?.*)/,
                'port' : /(?:^:)(\d+)((?:\/|$).*)/,
                'word' : /[^, ]+/g,
        };

    compare[ 'url' ] = parseUrl( url );
    compare[ 'baseUrl' ] = parseUrl( baseUrl );

    if ( different( 'proto' ) || different( 'domain' ) || different( 'port' ) ) return false;    //协议、域名、端口不同

    path = compare.url.path;
    basePath = compare.baseUrl.path;

    while ( path.length && path[ 0 ] == basePath[ 0 ] ) { //去除相同父目录
        path.shift();
        basePath.shift();
    }

    if ( path.length == 0 && basePath.length == 0 ) return './';     //两个url相同
    
    if ( path.length == 0 ) return parent( basePath.length );        //baseUrl是url的子目录

    if ( basePath.length == 0 ) return path.join( '/' ) + '/';        //url是baseUrl的子目录

    return parent( basePath.length ) + path.join( '/' ) + '/';        //url和baseUrl互不包含

    function different ( name ) {
        return compare[ 'url' ][ name ] != compare[ 'baseUrl' ][ name ];
    }

    function notValidUrl () {
        return compare[ 'url' ][ 'proto' ] == '' || compare[ 'baseUrl' ][ 'proto' ] == '';
    }

    function parseUrl ( url ) {
        var parsed = {};
        'proto,domain,port'.replace( r.word, function( name ){
            var match, reg = r[ name ];
            if ( !reg.test( url ) ) {
                parsed[ name ] = '';
                return;
            }
            match = url.match( reg );
            url = match[ 2 ];
            parsed[ name ] = match[ 1 ];
        });
        parsed[ 'path' ] = url.replace(/\/+$/,'').split('/');
        return parsed;
    }

    function parent ( level ) {
        console.log(level);
        var result = [];
        while ( level-- ) result.push( '../' );
        return result.join('');
    }
};

使用单元测试如下:

test("url和baseURL并非都是绝对路径或都是相对路径,没有结果", function() {
    expect(4);
    ok(!getRelativeURL('http://aoeu/ao','/aoeu'),'绝对,相对');
    ok(!getRelativeURL('/aoeu','http://aoeu/ao'),'相对,绝对');
    ok(getRelativeURL('http://aoeu','http://aoeu/ao'),'绝对,绝对');
    ok(getRelativeURL('/aoeu','/ao'),'相对,相对');
});
 
test("不在同一个域、同一个协议、同一个端口下,没有结果", function() {
    expect(3);
    ok(!getRelativeURL('http://aoc.com','http://aoeu.com'),'不同域');
    ok(!getRelativeURL('https://aoc.com','ftp://aoeu.com'),'不同协议');
    ok(!getRelativeURL('https://aoc.com:88','ftp://aoc.com:89'),'不同端口');
});

test("是否以/结尾不影响结果",function() {
    expect(3);
    equal(getRelativeURL('http://aoeu.com/aoeu/','http://aoeu.com/'),getRelativeURL('http://aoeu.com/aoeu','http://aoeu.com'),'都以/结尾');
    equal(getRelativeURL('http://aoeu.com/aoeu/','http://aoeu.com'),getRelativeURL('http://aoeu.com/aoeu','http://aoeu.com'),'url以/结尾');
    equal(getRelativeURL('http://aoeu.com/aoeu','http://aoeu.com/'),getRelativeURL('http://aoeu.com/aoeu','http://aoeu.com'),'baseUrl以/结尾');
})

test("相同目录",function(){
    expect(3);
    equal(getRelativeURL('http://aoeu.com','http://aoeu.com/'),'./','根目录');
    equal(getRelativeURL('http://aoeu.com/aoeu','http://aoeu.com/aoeu'),'./','单级');
    equal(getRelativeURL('http://aoeu.com/ao/eu','http://aoeu.com/ao/eu/'),'./','多级');
})

test("url和baseUrl中有一个为根目录",function() {
    expect(4);
    equal(getRelativeURL('http://aoeu.com/aoeu','http://aoeu.com'),'aoeu/','url为baseUrl的单级子目录');
    equal(getRelativeURL('http://aoeu.com/ao/eu','http://aoeu.com'),'ao/eu/','url为baseUrl的多级子目录');
    equal(getRelativeURL('http://aoeu.com','http://aoeu.com/ao'),'../','baseUrl为url的单级子目录');
    equal(getRelativeURL('http://aoeu.com','http://aoeu.com/ao/eu'),'../../','baseUrl为url的多级子目录'); 
!
})

test("其他",function() {
    expect(2);
    equal(getRelativeURL('http://aoeu.com/aoeu','http://aoeu.com/ueoa'),'../aoeu/','单级');
    equal(getRelativeURL('http://aoeu.com/ao/eu','http://aoeu.com/eu/ao'),'../../ao/eu/','多级');
})

如有更好解法或发现错误,请不吝赐教:)

This entry was posted in IT技术, Javascript. Bookmark the permalink.