function(baseURL, relativeURL, opts) {
      opts = opts || {};
      // remove any remaining space and CRLF
      baseURL = baseURL.trim();
      relativeURL = relativeURL.trim();
      if (!relativeURL) {
        // 2a) If the embedded URL is entirely empty, it inherits the
        // entire base URL (i.e., is set equal to the base URL)
        // and we are done.
        if (!opts.alwaysNormalize) {
          return baseURL;
        }
        var basePartsForNormalise = URLToolkit.parseURL(baseURL);
        if (!basePartsForNormalise) {
          throw new Error('Error trying to parse base URL.');
        }
        basePartsForNormalise.path = URLToolkit.normalizePath(basePartsForNormalise.path);
        return URLToolkit.buildURLFromParts(basePartsForNormalise);
      }
      var relativeParts = URLToolkit.parseURL(relativeURL);
      if (!relativeParts) {
        throw new Error('Error trying to parse relative URL.');
      }
      if (relativeParts.scheme) {
        // 2b) If the embedded URL starts with a scheme name, it is
        // interpreted as an absolute URL and we are done.
        if (!opts.alwaysNormalize) {
          return relativeURL;
        }
        relativeParts.path = URLToolkit.normalizePath(relativeParts.path);
        return URLToolkit.buildURLFromParts(relativeParts);
      }
      var baseParts = URLToolkit.parseURL(baseURL);
      if (!baseParts) {
        throw new Error('Error trying to parse base URL.');
      }
      if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') {
        // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc
        // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a'
        var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path);
        baseParts.netLoc = pathParts[1];
        baseParts.path = pathParts[2];
      }
      if (baseParts.netLoc && !baseParts.path) {
        baseParts.path = '/';
      }
      var builtParts = {
        // 2c) Otherwise, the embedded URL inherits the scheme of
        // the base URL.
        scheme: baseParts.scheme,
        netLoc: relativeParts.netLoc,
        path: null,
        params: relativeParts.params,
        query: relativeParts.query,
        fragment: relativeParts.fragment
      };
      if (!relativeParts.netLoc) {
        // 3) If the embedded URL's <net_loc> is non-empty, we skip to
        // Step 7.  Otherwise, the embedded URL inherits the <net_loc>
        // (if any) of the base URL.
        builtParts.netLoc = baseParts.netLoc;
        // 4) If the embedded URL path is preceded by a slash "/", the
        // path is not relative and we skip to Step 7.
        if (relativeParts.path[0] !== '/') {
          if (!relativeParts.path) {
            // 5) If the embedded URL path is empty (and not preceded by a
            // slash), then the embedded URL inherits the base URL path
            builtParts.path = baseParts.path;
            // 5a) if the embedded URL's <params> is non-empty, we skip to
            // step 7; otherwise, it inherits the <params> of the base
            // URL (if any) and
            if (!relativeParts.params) {
              builtParts.params = baseParts.params;
              // 5b) if the embedded URL's <query> is non-empty, we skip to
              // step 7; otherwise, it inherits the <query> of the base
              // URL (if any) and we skip to step 7.
              if (!relativeParts.query) {
                builtParts.query = baseParts.query;
              }
            }
          } else {
            // 6) The last segment of the base URL's path (anything
            // following the rightmost slash "/", or the entire path if no
            // slash is present) is removed and the embedded URL's path is
            // appended in its place.
            var baseURLPath = baseParts.path;
            var newPath = baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + relativeParts.path;
            builtParts.path = URLToolkit.normalizePath(newPath);
          }
        }
      }
      if (builtParts.path === null) {
        builtParts.path = opts.alwaysNormalize ? URLToolkit.normalizePath(relativeParts.path) : relativeParts.path;
      }
      return URLToolkit.buildURLFromParts(builtParts);
    }
function(baseURL, relativeURL, opts) {
      opts = opts || {};
      
      baseURL = baseURL.trim();
      relativeURL = relativeURL.trim();
      if (!relativeURL) {
        
        
        
        if (!opts.alwaysNormalize) {
          return baseURL;
        }
        var basePartsForNormalise = URLToolkit.parseURL(baseURL);
        if (!basePartsForNormalise) {
          throw new Error('Error trying to parse base URL.');
        }
        basePartsForNormalise.path = URLToolkit.normalizePath(basePartsForNormalise.path);
        return URLToolkit.buildURLFromParts(basePartsForNormalise);
      }
      var relativeParts = URLToolkit.parseURL(relativeURL);
      if (!relativeParts) {
        throw new Error('Error trying to parse relative URL.');
      }
      if (relativeParts.scheme) {
        
        
        if (!opts.alwaysNormalize) {
          return relativeURL;
        }
        relativeParts.path = URLToolkit.normalizePath(relativeParts.path);
        return URLToolkit.buildURLFromParts(relativeParts);
      }
      var baseParts = URLToolkit.parseURL(baseURL);
      if (!baseParts) {
        throw new Error('Error trying to parse base URL.');
      }
      if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') {
        
        
        var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path);
        baseParts.netLoc = pathParts[1];
        baseParts.path = pathParts[2];
      }
      if (baseParts.netLoc && !baseParts.path) {
        baseParts.path = '/';
      }
      var builtParts = {
        
        
        scheme: baseParts.scheme,
        netLoc: relativeParts.netLoc,
        path: null,
        params: relativeParts.params,
        query: relativeParts.query,
        fragment: relativeParts.fragment
      };
      if (!relativeParts.netLoc) {
        
        
        
        builtParts.netLoc = baseParts.netLoc;
        
        
        if (relativeParts.path[0] !== '/') {
          if (!relativeParts.path) {
            
            
            builtParts.path = baseParts.path;
            
            
            
            if (!relativeParts.params) {
              builtParts.params = baseParts.params;
              
              
              
              if (!relativeParts.query) {
                builtParts.query = baseParts.query;
              }
            }
          } else {
            
            
            
            
            var baseURLPath = baseParts.path;
            var newPath = baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + relativeParts.path;
            builtParts.path = URLToolkit.normalizePath(newPath);
          }
        }
      }
      if (builtParts.path === null) {
        builtParts.path = opts.alwaysNormalize ? URLToolkit.normalizePath(relativeParts.path) : relativeParts.path;
      }
      return URLToolkit.buildURLFromParts(builtParts);
    }
function ( baseURL , relativeURL , opts ) { opts = opts || { } ; // remove any remaining space and CRLF baseURL = baseURL . trim ( ) ; relativeURL = relativeURL . trim ( ) ; if ( ! relativeURL ) { // 2a) If the embedded URL is entirely empty, it inherits the // entire base URL (i.e., is set equal to the base URL) // and we are done. if ( ! opts . alwaysNormalize ) { return baseURL ; } var basePartsForNormalise = URLToolkit . parseURL ( baseURL ) ; if ( ! basePartsForNormalise ) { throw new Error ( 'Error trying to parse base URL.' ) ; } basePartsForNormalise . path = URLToolkit . normalizePath ( basePartsForNormalise . path ) ; return URLToolkit . buildURLFromParts ( basePartsForNormalise ) ; } var relativeParts = URLToolkit . parseURL ( relativeURL ) ; if ( ! relativeParts ) { throw new Error ( 'Error trying to parse relative URL.' ) ; } if ( relativeParts . scheme ) { // 2b) If the embedded URL starts with a scheme name, it is // interpreted as an absolute URL and we are done. if ( ! opts . alwaysNormalize ) { return relativeURL ; } relativeParts . path = URLToolkit . normalizePath ( relativeParts . path ) ; return URLToolkit . buildURLFromParts ( relativeParts ) ; } var baseParts = URLToolkit . parseURL ( baseURL ) ; if ( ! baseParts ) { throw new Error ( 'Error trying to parse base URL.' ) ; } if ( ! baseParts . netLoc && baseParts . path && baseParts . path [ 0 ] !== '/' ) { // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a' var pathParts = FIRST_SEGMENT_REGEX . exec ( baseParts . path ) ; baseParts . netLoc = pathParts [ 1 ] ; baseParts . path = pathParts [ 2 ] ; } if ( baseParts . netLoc && ! baseParts . path ) { baseParts . path = '/' ; } var builtParts = { // 2c) Otherwise, the embedded URL inherits the scheme of // the base URL. scheme : baseParts . scheme , netLoc : relativeParts . netLoc , path : null , params : relativeParts . params , query : relativeParts . query , fragment : relativeParts . fragment } ; if ( ! relativeParts . netLoc ) { // 3) If the embedded URL's <net_loc> is non-empty, we skip to // Step 7.  Otherwise, the embedded URL inherits the <net_loc> // (if any) of the base URL. builtParts . netLoc = baseParts . netLoc ; // 4) If the embedded URL path is preceded by a slash "/", the // path is not relative and we skip to Step 7. if ( relativeParts . path [ 0 ] !== '/' ) { if ( ! relativeParts . path ) { // 5) If the embedded URL path is empty (and not preceded by a // slash), then the embedded URL inherits the base URL path builtParts . path = baseParts . path ; // 5a) if the embedded URL's <params> is non-empty, we skip to // step 7; otherwise, it inherits the <params> of the base // URL (if any) and if ( ! relativeParts . params ) { builtParts . params = baseParts . params ; // 5b) if the embedded URL's <query> is non-empty, we skip to // step 7; otherwise, it inherits the <query> of the base // URL (if any) and we skip to step 7. if ( ! relativeParts . query ) { builtParts . query = baseParts . query ; } } } else { // 6) The last segment of the base URL's path (anything // following the rightmost slash "/", or the entire path if no // slash is present) is removed and the embedded URL's path is // appended in its place. var baseURLPath = baseParts . path ; var newPath = baseURLPath . substring ( 0 , baseURLPath . lastIndexOf ( '/' ) + 1 ) + relativeParts . path ; builtParts . path = URLToolkit . normalizePath ( newPath ) ; } } } if ( builtParts . path === null ) { builtParts . path = opts . alwaysNormalize ? URLToolkit . normalizePath ( relativeParts . path ) : relativeParts . path ; } return URLToolkit . buildURLFromParts ( builtParts ) ; }
jshint ignore:line If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or // E.g With opts.alwaysNormalize = false (default, spec compliant) http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g With opts.alwaysNormalize = true (not spec compliant) http://a.com/b/cd + /e/f/../g => http://a.com/e/g
jshint ignore : line If opts . alwaysNormalize is true then the path will always be normalized even when it starts with / or // E . g With opts . alwaysNormalize = false ( default spec compliant ) http : // a . com / b / cd + / e / f / .. / g = > http : // a . com / e / f / .. / g With opts . alwaysNormalize = true ( not spec compliant ) http : // a . com / b / cd + / e / f / .. / g = > http : // a . com / e / g