Source: util/navbuilder.js

/**
 * The basic idea of the nav builder is to support a chain of method calls that roughly map to the structure and categorisation
 * of parts of Bitbucket Server. This is referred to as the "Builder API" of navbuilder.
 *
 * E.g. `nav.project('foo').repo('bar').permissions()` will return a builder that can build the URL for the permissions page
 * of the repository with slug 'bar' of the project with key 'foo', while just `nav.project('foo')` will return a builder for a URL to
 * the project page for project 'foo'.
 *
 * At each point in the method chain, the returned object will support methods that return a builder
 * that maps to a concept at a lower level.
 * At most points, `.build()` may be called to build the URL to be used. There are a few places where no URL makes sense.
 *
 * The "Classes" listed in this module describe the methods that can be called from each step in the chain. E.g.
 *
 * ```javascript
 * var adminBuilder = nav.admin(); // adminBuilder will be a {@linkcode bitbucket/util/navbuilder.AdminBuilder}
 * var groupsBuilder = adminBuilder.groups(); // groupsBuilder will be a {@linkcode bitbucket/util/navbuilder.GroupsBuilder}
 * ```
 *
 * **Web Resource:** com.atlassian.bitbucket.server.bitbucket-web-api:navbuilder
 *
 * @example
 * require([
 *     'bitbucket/util/navbuilder',
 *     'bitbucket/util/server'
 * ], function (nav, server) {
 *     server.rest({
 *         url: nav.rest().projects().build()
 *     }).then(doSomethingWithProjects);
 * });
 *
 * @module bitbucket/util/navbuilder
 * @namespace bitbucket/util/navbuilder
 */
import * as AJS from '@atlassian/aui';

import $ from 'jquery';
import _ from 'lodash';
import Uri from 'lib/jsuri';
import DiffType from 'bitbucket/internal/model/diff-type';
import pageState from 'bitbucket/internal/model/page-state';
import Path from 'bitbucket/internal/model/path';

const { EFFECTIVE, COMMIT, RANGE } = DiffType;

// return a copy of the object without keys where the value is undefined
function omitUndefined(params) {
    return _.omitBy(params, _.isUndefined);
}

/**
 * Encapsulates a URI path and params that make up a query string.
 * This class is immutable - all mutating operations return a new instance.
 *
 * NOTE: The PathAndQuery constructor is not exposed.
 *
 * @private
 * @class PathAndQuery
 * @memberof bitbucket/util/navbuilder
 */
function PathAndQuery(components, params, anchor) {
    this.components = (_.isString(components) ? [components] : components) || [];
    this.params = params || {};
    this.anchor = anchor || undefined;
}

/**
 * Returns the described URL as a string. The returned URL is relative to the Bitbucket Server application's
 * context path root. E.g. If Bitbucket is running at example.com/bitbucket, the URL example.com/bitbucket/a/b/c
 * will be returned as /a/b/c.
 * In client-side code, this version of the URL is rarely useful. bitbucket/util/navbuilder.PathAndQuery.buildRelative is
 * likely to be what you want to use.
 *
 * @name bitbucket/util/navbuilder.PathAndQuery#buildRelNoContext
 * @returns {string}
 */
PathAndQuery.prototype.buildRelNoContext = function() {
    var path = '/' + _.map(this.components, encodeURIComponent).join('/');

    var params = _.reduce(
        this.params,
        function(memo, values, key) {
            if (!_.isArray(values)) {
                values = [values];
            }

            return memo.concat(
                _.map(values, function(value) {
                    return { key: key, value: value };
                })
            );
        },
        []
    );
    var query = _.map(params, function(param) {
        var encodedValue = encodeURIComponent(param.value);

        return encodeURIComponent(param.key) + (encodedValue ? '=' + encodedValue : '');
    }).join('&');

    return path + (query ? '?' + query : '') + (this.anchor ? '#' + encodeURI(this.anchor) : '');
};

/**
 * Returns the described URL as a string. The returned URL is relative to the server root. E.g.
 * If Bitbucket Server is running at example.com/bitbucket, the URL example.com/bitbucket/a/b/c
 * will be returned as /bitbucket/a/b/c.
 *
 * bitbucket/util/navbuilder.PathAndQuery.buildRelative
 * @returns {string}
 */
PathAndQuery.prototype.buildRelative = function() {
    return AJS.contextPath() + this.buildRelNoContext();
};

/**
 * Returns the described URL as a string. The returned URL is absolute. E.g.
 * If Bitbucket Server is running at example.com/bitbucket, the URL example.com/bitbucket/a/b/c
 * will be returned as http://example.com/bitbucket/a/b/c.
 *
 * bitbucket/util/navbuilder.PathAndQuery.buildAbsolute
 * @returns {string}
 */
PathAndQuery.prototype.buildAbsolute = function() {
    return (
        location.protocol +
        '//' +
        location.hostname +
        (location.port ? ':' + location.port : '') +
        this.buildRelative()
    );
};

PathAndQuery.prototype.toString = function() {
    return this.buildRelative();
};

/**
 * Adds query parameters. If a map (object) is supplied, its properties are added to the parameters.
 * If a single string is supplied, it is added as a query parameter with no value.
 *
 * @name bitbucket/util/navbuilder.PathAndQuery#addParams
 * @returns a new PathAndQuery object with the updated query params
 */
PathAndQuery.prototype.addParams = function(params) {
    var path = new PathAndQuery(this.components, _.assign({}, this.params));

    if (_.isString(params)) {
        path.params[params] = '';
    } else if (params) {
        if (params.hasOwnProperty('queryParams')) {
            path.params = _.assign(path.params, params.queryParams);
        } else if (!params.hasOwnProperty('urlMode')) {
            path.params = _.assign(path.params, params);
        } // todo - implement urlMode
    }

    return path;
};

/**
 * Sets the document hash. If a hash has been set previously, it is overwritten
 *
 * @name bitbucket/util/navbuilder.PathAndQuery#withFragment
 * @return a new PathAndQuery object with unchanged path and query string params, but with a new anchor
 */
PathAndQuery.prototype.withFragment = function(anchor) {
    return new PathAndQuery(this.components, this.params, anchor);
};

/**
 * Pushes a new path component onto the list of path components.
 *
 * @name bitbucket/util/navbuilder.PathAndQuery#pushComponents
 * @returns a new PathAndQuery object with the updated query params
 */
PathAndQuery.prototype.pushComponents = function() {
    var path = new PathAndQuery(this.components.slice(0), this.params);
    _.forEach(_.toArray(arguments).slice(0), function(component) {
        if (component !== '') {
            path.components.push(component);
        }
    });

    return path;
};

/**
 * A Builder of URLs. All methods in the Builder API will return a subclass of this and will include these methods.
 *
 *
 * NOTE: The Builder constructor is not exposed. A new Builder can be created through
 * {@linkcode bitbucket/util/navbuilder.newBuilder}.
 *
 * @class Builder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * Returns a new builder for the current path with the properties supported by otherMethods.
 * Used after each call in the method chain to construct the next object in the chain and specify
 * exactly which calls are acceptable.
 *
 * @name bitbucket/util/navbuilder.PathAndQuery#makeBuilder
 * @returns {bitbucket/util/navbuilder.Builder} a new builder
 */
PathAndQuery.prototype.makeBuilder = function(otherMethods) {
    var path = this;

    return _.assign(
        /** @lends bitbucket/util/navbuilder.Builder.prototype */
        {
            /**
             * @private
             */
            _path: function() {
                return path;
            },
            /**
             * @returns {string} relative URL. See bitbucket/util/navbuilder.PathAndQuery.buildRelative
             */
            build: function() {
                return path.buildRelative();
            },
            /**
             * @returns {string} absolute URL. See bitbucket/util/navbuilder.PathAndQuery.buildAbsolute
             */
            buildAbsolute: function() {
                return path.buildAbsolute();
            },
            /**
             * @returns {string} relative URL with not application context. Useful when calling libraries that add
             * the context automatically. See bitbucket/util/navbuilder.PathAndQuery.buildRelNoContext
             */
            buildNoContext: function() {
                return path.buildRelNoContext();
            },
            /**
             * @returns {bitbucket/util/navbuilder.Uri} URI object formed by the builder.
             */
            parse: function() {
                return parse(this.build());
            },
            /**
             * Sets query string parameters for the output URL of this builder.
             * @param {Object} params - A map of parameter name to value. Previous params with the same names will be overwritten.
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            withParams: function(params) {
                //return a new builder with the same methods as the current builder but with added query parameters
                return path.addParams(params).makeBuilder(otherMethods);
            },
            /**
             * Wrapper for `withParams` that filters out any params that are null or undefined
             * @param {Object} params - A map of parameter name to value. Previous params with the same names will be overwritten.
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            withDefinedParams: function(params) {
                return this.withParams(_.pickBy(params, param => param != null));
            },
            /**
             * Sets the hash (#) portion of the output URL for this builder.
             * @param {string} anchor - The hash portion of the URL.
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            withFragment: function(anchor) {
                return path.withFragment(anchor).makeBuilder(otherMethods);
            },
            /**
             * Return a new builder with more path components appended to the URL.
             * @param {...string} component - A path component to append.
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            addPathComponents: function() {
                //return a new builder with the same methods as the current builder but with an augmented (new) path
                return path.pushComponents.apply(path, arguments).makeBuilder(otherMethods);
            },
        },
        otherMethods
    );
};

// id/slug/key helpers

// If the input is a string, it's the key/slug/id.
// Otherwise, if it exists, assume it's a project/repo/PR
// Otherwise, use this pageState's project/repo/PR.

/**
 * @private
 */
function getProjectKey(projectOrKey) {
    if (typeof projectOrKey === 'string') {
        return projectOrKey;
    } else if (!projectOrKey) {
        throw new Error(AJS.I18n.getText('bitbucket.web.error.no.project'));
    }

    return projectOrKey.getKey ? projectOrKey.getKey() : projectOrKey.key;
}

/**
 * @private
 */
function getCurrentProject() {
    if (pageState.getProject()) {
        return pageState.getProject();
    }
    throw new Error(AJS.I18n.getText('bitbucket.web.error.no.project.context'));
}

/**
 * @private
 */
function getRepoSlug(repoOrSlug) {
    if (typeof repoOrSlug === 'string') {
        return repoOrSlug;
    } else if (!repoOrSlug) {
        throw new Error(AJS.I18n.getText('bitbucket.web.error.no.repo'));
    }

    return repoOrSlug.getSlug ? repoOrSlug.getSlug() : repoOrSlug.slug;
}

/**
 * @private
 */
function getHookKey(hookOrKey) {
    if (typeof hookOrKey === 'string') {
        return hookOrKey;
    } else if (!hookOrKey) {
        throw new Error(AJS.I18n.getText('bitbucket.web.error.no.hook.key'));
    }

    return hookOrKey.details.key;
}

/**
 * @private
 */
function getCurrentRepository() {
    if (pageState.getRepository()) {
        return pageState.getRepository();
    }
    throw new Error(AJS.I18n.getText('bitbucket.web.error.no.repo.context'));
}

/**
 * @private
 */
function getPullRequestId(prOrId) {
    if (prOrId !== 0 && !prOrId) {
        throw new Error(AJS.I18n.getText('bitbucket.web.error.no.pull-request.id'));
    }

    if (typeof prOrId in { string: 1, number: 1 }) {
        return prOrId;
    }

    return prOrId.getId ? prOrId.getId() : prOrId.id;
}

/**
 * @private
 */
function getCurrentPullRequest() {
    if (pageState.getPullRequest()) {
        return pageState.getPullRequest();
    }
    throw new Error(AJS.I18n.getText('bitbucket.web.error.no.pull-request.context'));
}

/**
 * If the project is a personal project, use the /users/slug form otherwise go with /projects/KEY form
 * @private
 */
function maybeResolveAsUserPath(path) {
    var projectKey = path.components[1];
    var userSlugPattern = /~(.*)/;
    var result = userSlugPattern.exec(projectKey);

    if (result) {
        return new PathAndQuery(['users', result[1].toLowerCase()]);
    }

    return path;
}

/**
 * pull path components from an arguments object.  We all .path('a', 'b') and .path(['a', 'b']) both.
 * @param {Array<string>} args
 * @private
 */
function componentsFromArguments(args) {
    //accept multiple args or accept a single arg that's an array to support .path('a', 'b') and .path(['a', 'b'])
    if (args.length === 1) {
        if (args[0] && args[0].components) {
            // accept a JSON.PathJSON
            return args[0].components;
        } else if (args[0] && args[0].getComponents) {
            // accept a Path object
            return args[0].getComponents();
        } else if ($.isArray(args[0])) {
            return args[0];
        }
    }

    return _.toArray(args);
}

//----------------------------------------
//Start of Builder API method chains
//----------------------------------------

//--- Methods at the root of the chain ---

/**
 * login-related {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class LoginBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.LoginBuilder}
 */
function login() {
    return new PathAndQuery('login').makeBuilder(
        /**
         * @lends bitbucket/util/navbuilder.LoginBuilder.prototype
         */
        {
            /**
             * @param {string} url - next URL to navigate to after login.
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            next: _nextUrl,
        }
    );
}

/**
 * @private
 */
function _nextUrl(url) {
    if (typeof url !== 'string') {
        var contextPath = AJS.contextPath();
        var currentPath = location.pathname;

        //Make current path relative if context path exist
        if (contextPath.length > 0) {
            currentPath = currentPath.substring(
                currentPath.indexOf(contextPath) + contextPath.length
            );
        }
        url = currentPath + location.search + location.hash;
    }

    return this._path()
        .addParams({ next: url })
        .makeBuilder();
}

/**
 * Temporary resource-related {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class TmpBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.TmpBuilder}
 */
function tmp() {
    return new PathAndQuery('tmp').makeBuilder(
        /**
         * @lends bitbucket/util/navbuilder.TmpBuilder.prototype
         */
        {
            /**
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            avatars: function tmpAvatars() {
                return this._path()
                    .pushComponents('avatars')
                    .makeBuilder();
            },
        }
    );
}

/**
 * Welcome page {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class WelcomeBuilder
 * @memberof bitbucket/util/navbuilder
 * @deprecated
 */

/**
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.WelcomeBuilder}
 */
function welcome() {
    return gettingStarted();
}

/**
 * Getting Started page {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class GettingStartedBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.GettingStartedBuilder}
 */
function gettingStarted() {
    return new PathAndQuery('getting-started').makeBuilder(
        /**
         * @lends bitbucket/util/navbuilder.GettingStartedBuilder.prototype
         */
        {
            /**
             * @param {string} url - next URL to navigate to after login.
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            next: _nextUrl,
        }
    );
}

/**
 * Search page {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class SearchBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.SearchBuilder}
 */
function search(terms) {
    // This is not a good way to build a path to /plugins/servlet/search, but this will look better once search is
    // usable at /search
    var builder = new PathAndQuery('plugins')
        .makeBuilder(
            /**
             * @lends bitbucket/util/navbuilder.GettingStartedBuilder.prototype
             */
            {
                /**
                 * @param {string} url - next URL to navigate to after login.
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                next: _nextUrl,
            }
        )
        .addPathComponents('servlet', 'search');

    if (terms) {
        builder = builder.withParams({ q: terms });
    }

    return builder;
}

/**
 * Admin-related {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class AdminBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.AdminBuilder}
 */
function admin() {
    return new PathAndQuery('admin').makeBuilder(
        /**
         * @lends bitbucket/util/navbuilder.AdminBuilder.prototype
         */
        {
            /**
             * @method
             * @returns {bitbucket/util/navbuilder.PermissionsBuilder}
             */
            permissions: permissions,
            /**
             * @method
             * @returns {bitbucket/util/navbuilder.AdminUsersBuilder}
             */
            users: adminUsers,
            /**
             * @method
             * @returns {bitbucket/util/navbuilder.GroupsBuilder}
             */
            groups: groups,
            /**
             * @method
             * @returns {bitbucket/util/navbuilder.LicenseBuilder}
             */
            licensing: function licensing() {
                /**
                 * License-related {@linkcode bitbucket/util/navbuilder.Builder}.
                 *
                 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                 *
                 * @class LicenseBuilder
                 * @memberof bitbucket/util/navbuilder
                 */

                return this._path()
                    .pushComponents('license')
                    .makeBuilder(
                        /**
                         * @lends bitbucket/util/navbuilder.LicenseBuilder.prototype
                         */
                        {
                            /**
                             * @returns {bitbucket/util/navbuilder.Builder}
                             */
                            edit: function editLicense() {
                                return this._path()
                                    .addParams({ edit: '' })
                                    .makeBuilder();
                            },
                        }
                    );
            },
            /**
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            mailServer: function mailServer() {
                return this._path()
                    .pushComponents('mail-server')
                    .makeBuilder();
            },
            /**
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            db: function adminDb() {
                return this._path()
                    .pushComponents('db')
                    .makeBuilder();
            },
            /**
             * @returns {bitbucket/util/navbuilder.Builder}
             * */
            storage: function adminStorage() {
                return this._path()
                    .pushComponents('storage')
                    .makeBuilder();
            },
            /**
             * @returns {bitbucket/util/navbuilder.Builder}
             * */
            rateLimitSettings: function adminRateLimitSettings() {
                return this._path()
                    .pushComponents('rate-limit')
                    .makeBuilder();
            },
        }
    );
}

/**
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.Builder}
 */
function allProjects() {
    return new PathAndQuery('projects').makeBuilder();
}

/**
 * {@linkcode bitbucket/util/navbuilder.Builder} for the Bitbucket Server-wide repository list.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class GlobalRepoBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * @name allRepos
 * @method
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.GlobalRepoBuilder}
 */
function globalAllRepos() {
    return new PathAndQuery('repos').makeBuilder(
        /**
         * @lends bitbucket/util/navbuilder.GlobalRepoBuilder
         */
        {
            /**
             * @param {string} visibility - A filter to the shown list of repositories: "public" or "private".
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            visibility: function allReposWithVisibility(visibility) {
                return this._path()
                    .addParams({ visibility: visibility })
                    .makeBuilder();
            },
        }
    );
}

/**
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.Builder}
 */
function captcha() {
    //Add a changing query param to ensure all browsers reload the image when refreshing it - some don't respect the HTTP headers
    return new PathAndQuery('captcha')
        .addParams({ ts: new Date().getTime().toString() })
        .makeBuilder();
}

/**
 * Project-related {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class ProjectBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * @param {string} projectKey - The key of the project to return a builder for.
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.ProjectBuilder}
 */
function project(projectOrKey) {
    var path = new PathAndQuery(['projects', getProjectKey(projectOrKey)]);

    return maybeResolveAsUserPath(path).makeBuilder(
        /**
         * @lends bitbucket/util/navbuilder.ProjectBuilder.prototype
         */
        {
            /**
             * @method
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            allRepos: allRepos,
            /**
             * @method
             * @param {string} slug - The slug of the repository to return a builder for.
             * @returns {bitbucket/util/navbuilder.RepositoryBuilder}
             */
            repo: repo,
            /**
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            createRepo: function createRepo() {
                return maybeResolveAsUserPath(this._path())
                    .pushComponents('repos')
                    .addParams('create')
                    .makeBuilder();
            },
            /**
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            settings: function projSettings() {
                return this._path()
                    .pushComponents('settings')
                    .makeBuilder({
                        /**
                         * @returns {bitbucket/util/navbuilder.Builder}
                         */
                        mergeStrategies: function() {
                            return this._path()
                                .pushComponents('merge-strategies', 'git')
                                .makeBuilder();
                        },
                    });
            },
            /**
             * @method
             * @returns {bitbucket/util/navbuilder.PermissionsBuilder}
             */
            permissions: permissions,
            /**
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            remove: function projDelete() {
                return this._path().makeBuilder();
            },
            /**
             * @method
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            hooks: restHooks,
            /**
             * @method
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            mergeChecks: mergeChecks,
            /**
             * @method
             * @returns {bitbucket/util/navbuilder.AdminUsersBuilder}
             */
            users: adminUsers,
            /**
             * @method
             * @returns {bitbucket/util/navbuilder.GroupsBuilder}
             */
            groups: groups,
            /**
             * @param {number} [size] - A pixel value for the height and width of the avatar. Please use only pixel sizes referenced by the ADG at http://developer.atlassian.com/design.
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            avatar: function projAvatar(size) {
                var builder = this._path().pushComponents('avatar.svg');

                if (size) {
                    builder = builder.addParams({ s: size });
                }

                return builder.makeBuilder();
            },
        }
    );
}

/**
 * Returns the project builder for the current page's project, if there is one.
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.ProjectBuilder}
 */
function currentProject() {
    return project(getCurrentProject());
}

/**
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.Builder}
 */
function createProject() {
    return new PathAndQuery('projects').addParams('create').makeBuilder();
}

/**
 * Shorthand for `.project(repository.project).repository(repository)`
 * @memberof bitbucket/util/navbuilder
 * @param {Object} repository - The full repository object
 * @returns {bitbucket/util/navbuilder.RepositoryBuilder}
 */
function repositoryShorthand(repository) {
    return this.project(repository.project).repo(repository);
}

/**
 * Shorthand for `.project(pullRequest.toRef.repository.project).repository(pullRequest.toRef.repository).pullRequest(pullRequest)`
 * @memberof bitbucket/util/navbuilder
 * @param {Object} pullRequest - The full pull request object
 * @returns {bitbucket/util/navbuilder.RepositoryBuilder}
 */
function pullRequestShorthand(pullRequest) {
    const repository = pullRequest.toRef.repository;

    return this.project(repository.project)
        .repo(repository)
        .pullRequest(pullRequest);
}

/**
 * REST URL {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class RestBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * Builds REST URLs (e.g. URLs handled by a <rest> plugin module). This method has some shortcuts to help you build
 * common URL shapes for your plugin, and also shapes that are used by the Bitbucket Server core REST APIs. For example, to build a
 * URL like: 'CONTEXT_PATH/my-rest/1.0/projects/PROJ/repos/REPO/pull-requests/1/a/b' while viewing a pull request, use
 *
 * @example
 * navbuilder.rest('my-rest', '1.0').currentPullRequest().addPathComponents('a', 'b').build();
 *
 * @memberof bitbucket/util/navbuilder
 * @param {String} [resourcePathComponent=api] - The REST module path to build URLs for. The default, "api" points to Bitbucket Server's core REST API path
 * @param {String} [version=latest] - The version of the API to reference. Since 3.4
 * @returns {bitbucket/util/navbuilder.RestBuilder}
 */
function rest(resourcePathComponent, version) {
    resourcePathComponent = resourcePathComponent || 'api';
    version = version || 'latest';

    return new PathAndQuery(['rest', resourcePathComponent, version]).makeBuilder(
        /**
         * @lends bitbucket/util/navbuilder.RestBuilder.prototype
         */
        {
            /**
             * @method
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            projects: function() {
                return this._path()
                    .pushComponents('projects')
                    .makeBuilder();
            },
            /**
             * @method
             * @param {string} key - The key of the project to form URLs for.
             * @returns {bitbucket/util/navbuilder.RestProjectBuilder}
             */
            project: restProj,
            /**
             * Shorthand for `.project(repository.project).repository(repository)`
             * @method
             * @param {Object} repository - The full repository object
             * @returns {bitbucket/util/navbuilder.RestRepositoryBuilder}
             */
            repository: repositoryShorthand,
            /**
             * Shorthand for `.project(pullRequest.toRef.repository.project).repository(pullRequest.toRef.repository).pullRequest(pullRequest)`
             * @method
             * @param {Object} pullRequest - The full pull request object
             * @returns {bitbucket/util/navbuilder.RestRepositoryBuilder}
             */
            pullRequest: pullRequestShorthand,
            /**
             * @method
             * @returns {bitbucket/util/navbuilder.RestProjectBuilder}
             */
            currentProject: restCurrentProject,
            /**
             * @method
             * @returns {bitbucket/util/navbuilder.RestRepositoryBuilder}
             */
            currentRepo: restCurrentRepo,
            /**
             * @method
             * @returns {bitbucket/util/navbuilder.RestPullRequestBuilder}
             */
            currentPullRequest: restCurrentPullRequest,
            /**
             * @returns {bitbucket/util/navbuilder.RestMarkupBuilder}
             */
            markup: function restMarkup() {
                /**
                 * REST markup URL {@linkcode bitbucket/util/navbuilder.Builder}.
                 *
                 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                 *
                 * @class RestMarkupBuilder
                 * @memberof bitbucket/util/navbuilder
                 */
                return this._path()
                    .pushComponents('markup')
                    .makeBuilder(
                        /**
                         * @lends bitbucket/util/navbuilder.RestMarkupBuilder.prototype
                         */
                        {
                            /**
                             * @returns {bitbucket/util/navbuilder.Builder}
                             */
                            preview: function restMarkupPreview() {
                                return this._path()
                                    .pushComponents('preview')
                                    .makeBuilder();
                            },
                        }
                    );
            },
            /**
             * @returns {bitbucket/util/navbuilder.RestProfileBuilder}
             */
            profile: function restProfile() {
                /**
                 * REST profile URL {@linkcode bitbucket/util/navbuilder.Builder}.
                 *
                 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                 *
                 * @class RestProfileBuilder
                 * @memberof bitbucket/util/navbuilder
                 */
                return this._path()
                    .pushComponents('profile')
                    .makeBuilder(
                        /**
                         * @lends bitbucket/util/navbuilder.RestProfileBuilder.prototype
                         */
                        {
                            /**
                             * REST recently viewed URL {@linkcode bitbucket/util/navbuilder.Builder}.
                             *
                             * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                             *
                             * @class RestRecentlyViewedBuilder
                             * @memberof bitbucket/util/navbuilder
                             */
                            /**
                             * @returns {bitbucket/util/navbuilder.RestRecentlyViewedBuilder}
                             */
                            recent: function restProfileRecent() {
                                return this._path()
                                    .pushComponents('recent')
                                    .makeBuilder(
                                        /**
                                         * @lends bitbucket/util/navbuilder.RestRecentlyViewedBuilder.prototype
                                         */
                                        {
                                            /**
                                             * @returns {bitbucket/util/navbuilder.Builder}
                                             */
                                            repos: function restProfileRecentRepos() {
                                                return this._path()
                                                    .pushComponents('repos')
                                                    .makeBuilder();
                                            },
                                        }
                                    );
                            },
                        }
                    );
            },
            /**
             * @returns {bitbucket/util/navbuilder.RestRawContentBuilder}
             */
            raw: function restRawContent() {
                /**
                 * REST markup URL {@linkcode bitbucket/util/navbuilder.Builder}.
                 *
                 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                 *
                 * @class RestRawContentBuilder
                 * @memberof bitbucket/util/navbuilder
                 */
                return this._path()
                    .pushComponents('raw')
                    .makeBuilder(
                        /**
                         * @lends bitbucket/util/navbuilder.RestRawContentBuilder.prototype
                         */
                        {
                            /**
                             * @returns {bitbucket/util/navbuilder.Builder}
                             */
                            path: function path(filePath) {
                                var path = this._path();

                                return path.pushComponents
                                    .apply(path, componentsFromArguments(filePath))
                                    .makeBuilder();
                            },
                        }
                    );
            },
            /**
             * @param {string} [userSlug] - If provided, a RestUserBuilder will be returned. Otherwise, a regular builder is returned.
             * @returns {bitbucket/util/navbuilder.Builder|bitbucket/util/navbuilder.RestUserBuilder}
             */
            users: function restUsers(userSlug) {
                var builder = this._path().pushComponents('users');

                if (userSlug) {
                    /**
                     * {@linkcode bitbucket/util/navbuilder.Builder} for REST user URLs.
                     *
                     * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                     *
                     * @class RestUserBuilder
                     * @memberof bitbucket/util/navbuilder
                     */
                    return builder.pushComponents(userSlug).makeBuilder(
                        /**
                         * @lends bitbucket/util/navbuilder.RestUserBuilder.prototype
                         */
                        {
                            /**
                             * @method
                             * @param {number} [size] - A pixel value for the height and width of the avatar. Please use only pixel sizes referenced by the ADG at http://developer.atlassian.com/design.
                             * @returns {bitbucket/util/navbuilder.Builder}
                             */
                            avatar: userAvatar,
                            repos: function restUserRepos() {
                                return this._path()
                                    .pushComponents('repos')
                                    .makeBuilder();
                            },
                        }
                    );
                }

                return builder.makeBuilder();
            },
            /**
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            groups: function restGroups() {
                return this._path()
                    .pushComponents('groups')
                    .makeBuilder();
            },
            /**
             * @method
             * @returns {bitbucket/util/navbuilder.RestHookPluginsBuilder}
             */
            hooks: restHookPlugins,
            /**
             * @method
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            allRepos: allRepos,
            /**
             * @returns {bitbucket/util/navbuilder.RestAdminBuilder}
             */
            admin: function restAdmin() {
                /**
                 * Admin REST URL {@linkcode bitbucket/util/navbuilder.Builder}.
                 *
                 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                 *
                 * @class RestAdminBuilder
                 * @memberof bitbucket/util/navbuilder
                 */
                return this._path()
                    .pushComponents('admin')
                    .makeBuilder(
                        /**
                         * @lends bitbucket/util/navbuilder.RestAdminBuilder.prototype
                         */
                        {
                            /**
                             * @returns {bitbucket/util/navbuilder.Builder}
                             */
                            users: function restAdminUsers() {
                                return this._path()
                                    .pushComponents('users')
                                    .makeBuilder(
                                        /**
                                         * @lends bitbucket/util/navbuilder.RestAdminBuilder.prototype
                                         */
                                        {
                                            /**
                                             * @returns {bitbucket/util/navbuilder.Builder}
                                             */
                                            erasure: function restAdminUsersErasure(name) {
                                                return this._path()
                                                    .pushComponents('erasure')
                                                    .addParams({ name: name })
                                                    .makeBuilder();
                                            },
                                        }
                                    );
                            },
                            rateLimitSettings: function restRateLimitSettings() {
                                return this._path()
                                    .pushComponents('rate-limit', 'settings')
                                    .makeBuilder();
                            },
                        }
                    );
            },
        }
    );
}

/**
 * Add-on-related {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class AddonBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.AddonBuilder}
 */
function addons() {
    return new PathAndQuery(['plugins', 'servlet', 'upm']).makeBuilder(
        /**
         * @lends bitbucket/util/navbuilder.AddonBuilder.prototype
         */
        {
            /**
             * Add-on-markeplace-related {@linkcode bitbucket/util/navbuilder.Builder}.
             *
             * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
             *
             * @class AddonMarketplaceBuilder
             * @memberof bitbucket/util/navbuilder
             */

            /**
             * @returns {bitbucket/util/navbuilder.AddonMarketplaceBuilder}
             */
            marketplace: function addonsRequests() {
                return this._path()
                    .pushComponents('marketplace', 'popular')
                    .makeBuilder(
                        /**
                         * @lends bitbucket/util/navbuilder.AddonMarketplaceBuilder.prototype
                         */
                        {
                            category: addonCategory,
                        }
                    );
            },
            /**
             * Add-on-requests-related {@linkcode bitbucket/util/navbuilder.Builder}.
             *
             * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
             *
             * @class AddonRequestsBuilder
             * @memberof bitbucket/util/navbuilder
             */

            /**
             * @returns {bitbucket/util/navbuilder.AddonRequestsBuilder}
             */
            requests: function addonsRequests() {
                return this._path()
                    .pushComponents('requests', 'popular')
                    .makeBuilder(
                        /**
                         * @lends bitbucket/util/navbuilder.AddonRequestsBuilder.prototype
                         */
                        {
                            category: addonCategory,
                        }
                    );
            },
        }
    );
}

/**
 * @param {string} category - The category of requests to filter by.
 * @returns {bitbucket/util/navbuilder.Builder}
 */
function addonCategory(category) {
    return this._path()
        .addParams({ category })
        .makeBuilder();
}

/**
 * {@linkcode bitbucket/util/navbuilder.Builder} for creating plugin servlet URLs.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class PluginServletsBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.PluginServletsBuilder}
 */
function pluginServlets() {
    return new PathAndQuery(['plugins', 'servlet']).makeBuilder(
        /**
         * @lends bitbucket/util/navbuilder.PluginServletsBuilder.prototype
         */
        {
            /**
             * @param {...string} component - A path component under the servlet.
             * @returns {bitbucket/util/navbuilder.PluginServletsPathBuilder}
             */
            path: function servletPath() {
                /**
                 * PluginServlet path-related {@linkcode bitbucket/util/navbuilder.Builder}.
                 *
                 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                 *
                 * @class PluginServletsPathBuilder
                 * @memberof bitbucket/util/navbuilder
                 */
                var path = this._path();

                return path.pushComponents
                    .apply(path, componentsFromArguments(arguments))
                    .makeBuilder(
                        /**
                         * @lends bitbucket/util/navbuilder.PluginServletsPathBuilder.prototype
                         */
                        {
                            /**
                             * @method
                             * @returns {bitbucket/util/navbuilder.PluginServletsProjectBuilder}
                             */
                            currentProject: pluginCurrentProject,
                            /**
                             * @method
                             * @returns {bitbucket/util/navbuilder.Builder}
                             */
                            currentRepo: pluginCurrentRepo,
                        }
                    );
            },
        }
    );
}

/**
 * PluginServlet project-related {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class PluginServletsProjectBuilder
 * @memberof bitbucket/util/navbuilder
 */
/**
 * Documented at parent builder
 * @private
 */
function pluginProj(projectOrKey) {
    var key = getProjectKey(projectOrKey);

    return this._path()
        .pushComponents('projects', key)
        .makeBuilder(
            /**
             * @lends bitbucket/util/navbuilder.PluginServletsProjectBuilder.prototype
             */
            {
                /**
                 * @method
                 * @param {string} slug - The slug of the repo to form URLs for.
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                repo: pluginRepo,
            }
        );
}

/**
 * Documented at parent builder
 * @private
 */
function pluginCurrentProject() {
    return pluginProj.call(this, getCurrentProject());
}

/**
 * Documented at parent builder
 * @private
 */
function pluginCurrentRepo() {
    return pluginCurrentProject.call(this).repo(getCurrentRepository());
}

/**
 * Documented at parent builders
 * @private
 */
function pluginRepo(repoOrSlug) {
    var slug = getRepoSlug(repoOrSlug);

    return this._path()
        .pushComponents('repos', slug)
        .makeBuilder();
}

/**
 * permissions-related {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class PermissionsBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * Documented at parent builders
 * @private
 */
function permissions() {
    return this._path()
        .pushComponents('permissions')
        .makeBuilder(
            /**
             * @lends bitbucket/util/navbuilder.PermissionsBuilder.prototype
             */
            {
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.PermissionBuilder}
                 */
                permission: permission,
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.AdminUsersBuilder}
                 */
                users: adminUsers,
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.GroupsBuilder}
                 */
                groups: groups,
            }
        );
}

/**
 * permission-related {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class PermissionBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * Documented at parent builders
 * @private
 */
function permission(name) {
    return this._path()
        .pushComponents(name)
        .makeBuilder(
            /**
             * @lends bitbucket/util/navbuilder.PermissionBuilder.prototype
             */
            {
                /**
                 * @returns bitbucket/util/navbuilder.Builder
                 */
                users: function usersPerm() {
                    return this._path()
                        .pushComponents('users')
                        .makeBuilder();
                },
                /**
                 * @returns bitbucket/util/navbuilder.Builder
                 */
                groups: function groupsPerm() {
                    return this._path()
                        .pushComponents('groups')
                        .makeBuilder();
                },
                /**
                 * @returns bitbucket/util/navbuilder.Builder
                 */
                all: function allPerm() {
                    return this._path()
                        .pushComponents('all')
                        .makeBuilder();
                },
            }
        );
}

/**
 * Admin user-related {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class AdminUsersBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * Documented at parent builders
 * @private
 */
function adminUsers() {
    return this._path()
        .pushComponents('users')
        .makeBuilder(
            /**
             * @lends bitbucket/util/navbuilder.AdminUsersBuilder.prototype
             */
            {
                /**
                 * @method
                 * @returns bitbucket/util/navbuilder.Builder
                 */
                anonymize,
                /**
                 * @method
                 * @returns bitbucket/util/navbuilder.Builder
                 */
                create: createEntity,
                /**
                 * @method
                 * @param {string} name - The username of the user to delete.
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                deleteUser: deleteEntity,
                /**
                 * @param {string} name - The username of the user to read/write captcha info for.
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                captcha: function adminCaptcha(name) {
                    return this._path()
                        .pushComponents('captcha')
                        .addParams({ name: name })
                        .makeBuilder();
                },
                /**
                 * @method
                 * @param {string} name - The username of the user to view.
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                view: viewEntity,
                /**
                 * @method
                 * @param {string} filter - A search string to filter by.
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                filter: filterEntity,
                /**
                 * @private
                 * @param {string} deletedUser - The user who was just deleted.
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                deleteSuccess: deleteSuccess,
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.PermissionsBuilder}
                 */
                permissions: permissions,
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                none: nonePerm,
            }
        );
}

/**
 * Group-related {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class GroupsBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * @private
 */
function groups() {
    return this._path()
        .pushComponents('groups')
        .makeBuilder(
            /**
             * @lends bitbucket/util/navbuilder.GroupsBuilder.prototype
             */
            {
                /**
                 * @method
                 * @returns bitbucket/util/navbuilder.Builder
                 */
                create: createEntity,
                /**
                 * @method
                 * @param {string} name - The name of the group to delete.
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                deleteGroup: deleteEntity,
                /**
                 * @method
                 * @param {string} name - The name of the group to view.
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                view: viewEntity,
                /**
                 * @method
                 * @param {string} filter - A search string to filter by.
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                filter: filterEntity,
                /**
                 * @private
                 * @param {string} deletedUser - The user who was just deleted.
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                deleteSuccess: deleteSuccess,
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.PermissionsBuilder}
                 */
                permissions: permissions,
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                none: nonePerm,
            }
        );
}

//--- Methods further down the chain ---

/**
 * Documented at parent builders
 * @private
 */
function nonePerm() {
    return this._path()
        .pushComponents('none')
        .makeBuilder();
}

/**
 * Documented at parent builders
 * @private
 */
function allRepos() {
    return maybeResolveAsUserPath(this._path())
        .pushComponents('repos')
        .makeBuilder();
}

/**
 * User-related {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class UserBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * @memberof bitbucket/util/navbuilder
 * @param userOrSlug - The URL-safe slug for a user or a full user object.
 * @returns {bitbucket/util/navbuilder.UserBuilder}
 */
function user(userOrSlug) {
    var subpath = 'users';
    var slug;

    if (typeof userOrSlug === 'string') {
        slug = userOrSlug;
    } else {
        slug = userOrSlug.slug;
        if (userOrSlug.type === 'SERVICE') {
            subpath = 'bots';
        }
    }

    return new PathAndQuery([subpath, slug]).makeBuilder(
        /**
         * @lends bitbucket/util/navbuilder.UserBuilder.prototype
         */
        {
            /**
             * @method
             * @param {number} [size] - A pixel value for the height and width of the avatar. Please use only pixel sizes referenced by the ADG at http://developer.atlassian.com/design.
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            avatar: userAvatar,
        }
    );
}

/**
 * Documented at parent builders
 * @private
 */
function anonymize() {
    return this._path()
        .pushComponents('anonymize')
        .makeBuilder();
}

/**
 * Documented at parent builders
 * @private
 */
function createEntity() {
    return this._path()
        .addParams({ create: '' })
        .makeBuilder();
}

/**
 * Documented at parent builders
 * @private
 */
function deleteEntity(name) {
    // delete is a reserved keyword
    return this._path()
        .addParams({ name: name })
        .makeBuilder();
}

/**
 * Documented at parent builds
 * @private
 */
function viewEntity(name) {
    return this._path()
        .pushComponents('view')
        .addParams({ name: name })
        .makeBuilder();
}

/**
 * Documented at parent builds
 * @private
 */
function filterEntity(filterValue) {
    return this._path()
        .addParams({ filter: filterValue })
        .makeBuilder();
}

/**
 * Documented at parent builds
 * @private
 */
function deleteSuccess(name) {
    return this._path()
        .addParams({ deleted: name })
        .makeBuilder();
}

/**
 * Repository-related {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class RepositoryBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * @param {string} slug - The slug of the repository to return a builder for.
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.RepositoryBuilder}
 */
function repo(repoOrSlug) {
    return maybeResolveAsUserPath(this._path())
        .pushComponents('repos', getRepoSlug(repoOrSlug))
        .makeBuilder(
            /**
             * @lends bitbucket/util/navbuilder.RepositoryBuilder.prototype
             */
            {
                /**
                 * @returns {bitbucket/util/navbuilder.RepoBrowseBuilder}
                 */
                browse: function repoBrowse() {
                    return this._path()
                        .pushComponents('browse')
                        .makeBuilder(RepoBrowseBuilderMethods);
                },
                /**
                 * @returns {bitbucket/util/navbuilder.RepoBrowseBuilder}
                 */
                raw: function repoBrowse() {
                    return this._path()
                        .pushComponents('raw')
                        .makeBuilder(RepoBrowseBuilderMethods);
                },
                /**
                 * @method
                 * @param {string|JSON.FileChangeJSON} pathOrFileChange - A string to use as the file path to diff, or a FileChangeJSON to specify the revisions and paths together.
                 * @returns {bitbucket/util/navbuilder.RevisionSpecifyingBuilder}
                 */
                diff: repoDiff,
                /**
                 * @returns {bitbucket/util/navbuilder.CommitsBuilder}
                 */
                commits: function repoCommits() {
                    /**
                     * Commits list-related {@linkcode bitbucket/util/navbuilder.Builder}.
                     *
                     * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                     *
                     * @class CommitsBuilder
                     * @memberof bitbucket/util/navbuilder
                     */
                    return this._path()
                        .pushComponents('commits')
                        .makeBuilder(
                            /**
                             * @lends bitbucket/util/navbuilder.CommitsBuilder.prototype
                             */
                            {
                                /**
                                 * @param {string} [until] - The ID of the ref to use as the tip of the commit list. Omit to use the default branch's HEAD.
                                 * @returns {bitbucket/util/navbuilder.Builder}
                                 */
                                until: function repoCommitsUntil(until) {
                                    var builder = this._path();

                                    if (until && !until.isDefault) {
                                        if (typeof until !== 'string') {
                                            until = until.displayId || until;
                                        }
                                        builder = builder.addParams({
                                            until: until,
                                        });
                                    }

                                    return builder.makeBuilder();
                                },
                            }
                        );
                },
                /**
                 * @param {string} [baseRef] - The ID of the ref to use as the base branch for comparisons.
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                branches: function repoBranches(baseRef) {
                    var builder = this._path().pushComponents('branches');

                    if (baseRef && !baseRef.isDefault) {
                        if (typeof baseRef !== 'string') {
                            baseRef = baseRef.displayId || baseRef.id || baseRef;
                        }
                        builder = builder.addParams({ base: baseRef });
                    }

                    return builder.makeBuilder();
                },
                /**
                 * @method
                 * @param {string} commitId - The ID of the commit to form URLs for.
                 * @returns {bitbucket/util/navbuilder.CommitBuilder}
                 */
                commit: repoCommit,
                /**
                 * {@linkcode bitbucket/util/navbuilder.Builder} for branch comparison URLs.
                 *
                 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                 *
                 * @class BranchCompareBuilder
                 * @extends bitbucket/util/navbuilder.SourceTargetBuilder
                 * @memberof bitbucket/util/navbuilder
                 */
                /**
                 * @returns {bitbucket/util/navbuilder.BranchCompareBuilder}
                 */
                compare: function repoCompare() {
                    function comparePath(path) {
                        return function() {
                            //noinspection JSPotentiallyInvalidUsageOfThis
                            return this._path()
                                .pushComponents(path)
                                .makeBuilder(secondBranchParamBuilders);
                        };
                    }

                    return this._path()
                        .pushComponents('compare')
                        .makeBuilder(
                            _.assign(
                                /**
                                 * @lends bitbucket/util/navbuilder.BranchCompareBuilder.prototype
                                 */
                                {
                                    /**
                                     * @method
                                     * @returns {bitbucket/util/navbuilder.SourceTargetBuilder}
                                     */
                                    commits: comparePath('commits'),
                                    /**
                                     * @method
                                     * @returns {bitbucket/util/navbuilder.SourceTargetBuilder}
                                     */
                                    diff: comparePath('diff'),
                                },
                                compareDefaultBranchParamBuilders
                            )
                        );
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                settings: function repoSettings() {
                    return this._path()
                        .pushComponents('settings')
                        .makeBuilder();
                },
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.PermissionsBuilder}
                 */
                permissions: permissions,
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                hooks: restHooks,
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                mergeChecks: mergeChecks,
                /**
                 * @param {string} scm - The kind of SCM (e.g. git) for this repo.
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                clone: function repoClone(scm) {
                    var path = this._path();
                    var projectKey = path.components[1].toLowerCase();
                    var repoSlug = path.components[3].toLowerCase();

                    if (path.components[0] === 'users') {
                        projectKey = '~' + projectKey;
                    }

                    return new PathAndQuery(
                        ['scm', projectKey, repoSlug + '.' + scm],
                        path.params
                    ).makeBuilder();
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                fork: function repoFork() {
                    return this._path()
                        .addParams('fork')
                        .makeBuilder();
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                forks: function repoForks() {
                    return this._path()
                        .addParams('forks')
                        .makeBuilder();
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                allPullRequests: function allPullRequests() {
                    return this._path()
                        .pushComponents('pull-requests')
                        .makeBuilder({
                            /**
                             * @memberof bitbucket/util/navbuilder
                             * @returns {bitbucket/util/navbuilder.Builder}
                             */
                            state: function state(value) {
                                return this._path()
                                    .addParams({ state: value })
                                    .makeBuilder();
                            },
                        });
                },
                /**
                 * @returns {bitbucket/util/navbuilder.SourceTargetBuilder}
                 */
                createPullRequest: function createPullRequest() {
                    return this._path()
                        .pushComponents('pull-requests')
                        .addParams('create')
                        .makeBuilder(secondBranchParamBuilders);
                },
                /**
                 * @method
                 * @param {number} pullRequestId - The ID of a pull request to build URLs for.
                 * @returns {bitbucket/util/navbuilder.PullRequestBuilder}
                 */
                pullRequest: pullRequest,
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                attachments: function repoAttachments() {
                    return this._path()
                        .pushComponents('attachments')
                        .makeBuilder();
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                sizes: function repoSizes() {
                    return this._path()
                        .pushComponents('sizes')
                        .makeBuilder();
                },
                /**
                 * Documented under Builder
                 * @private
                 */
                build: function() {
                    return this._path()
                        .pushComponents('browse')
                        .toString(); //the stem /projects/PROJ/repos is different to the path needed if build() is called
                },
            }
        );
}

/**
 * Returns the builder for the current page's repository, if there is one.
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.RepositoryBuilder}
 */
function currentRepo() {
    return currentProject().repo(pageState.getRepository());
}

/**
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.Builder}
 */
function dashboard() {
    return new PathAndQuery('dashboard').makeBuilder();
}

/**
 * {@linkcode bitbucket/util/navbuilder.Builder} to select which commits are involved and whether to retrieve the
 * "raw" version when browsing files within Bitbucket Server.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class RevisionSpecifyingBuilder
 * @memberof bitbucket/util/navbuilder
 */

var RevisionRangeSpecifyingBuilderMethods =
    /**
     * @lends bitbucket/util/navbuilder.RevisionSpecifyingBuilder.prototype
     */
    {
        /**
         * Set the "at" revision for browsing. This revision describes the branch head whose history is being viewed.
         * This is _not_ the actual revision at which to view the file (but it is used as fallback for that purpose).
         * The main purpose of this parameter is to determine which branch to use as "current" in the page layout.
         *
         * @param {string} refId - The ID of a ref or commit in the SCM whose history to browse.
         * @returns {bitbucket/util/navbuilder.RevisionSpecifyingBuilder}
         */
        at: function repoBrowsePathAt(refId) {
            var builder = this._path();

            if (refId) {
                if (typeof refId !== 'string') {
                    refId = refId.displayId || refId;
                }
                builder = builder.addParams({ at: refId });
            }

            return builder.makeBuilder(RevisionRangeSpecifyingBuilderMethods);
        },
        /**
         * Set the "until" commit for browsing. This commit describes the actual revision at which you want to view file content.
         * @param {string} commitId - The ID of a commit in the SCM at which to browse.
         * @param {string} path - the path at this commit. Since we're following renames, the path at this commit might be different from at the head.
         * @returns {bitbucket/util/navbuilder.RevisionSpecifyingBuilder}
         */
        until: function repoBrowsePathUntil(commitId, path) {
            return this._path()
                .addParams(
                    omitUndefined({
                        until: commitId,
                        untilPath: path ? path.toString() : undefined,
                    })
                )
                .makeBuilder(RevisionRangeSpecifyingBuilderMethods);
        },
        /**
         * Describes that you want to view the "raw"/downloadable version of the file, not the HTML version
         * @returns {bitbucket/util/navbuilder.RevisionSpecifyingBuilder}
         */
        raw: function repoBrowsePathRaw() {
            return this._path()
                .addParams({ raw: '' })
                .makeBuilder(RevisionRangeSpecifyingBuilderMethods);
        },
    };

/**
 * {@linkcode bitbucket/util/navbuilder.Builder} to select where to browse files.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class RepoBrowseBuilder
 * @memberof bitbucket/util/navbuilder
 */
var RepoBrowseBuilderMethods =
    /**
     * @lends bitbucket/util/navbuilder.RepoBrowseBuilder.prototype
     */
    {
        /**
         * @param {...string} components - path components for the file/directory at which to browse.
         * @returns {bitbucket/util/navbuilder.RevisionSpecifyingBuilder}
         */
        path: function repoBrowsePath() {
            var path = this._path();

            return path.pushComponents
                .apply(path, componentsFromArguments(arguments))
                .makeBuilder(RevisionRangeSpecifyingBuilderMethods);
        },
        /**
         * Set the "at" revision for browsing. This revision describes the branch head whose history is being viewed.
         * This is _not_ the actual revision at which to view the file (but it is used as fallback for that purpose).
         * The main purpose of this parameter is to determine which branch to use as "current" in the page layout.
         *
         * @method
         * @param {string} refId - The ID of a ref or commit in the SCM whose history to browse.
         * @returns {bitbucket/util/navbuilder.RevisionSpecifyingBuilder}
         */
        at: RevisionRangeSpecifyingBuilderMethods.at,
    };

/**
 * Documented at parent builders
 * @private
 */
function repoDiff(fileChangeOrPath, headRef, headPath, autoSincePath) {
    var builder = this._path();
    var path;

    // Duck-type as adding FileChange as a dependency on navbuilder causes too much recursion in dependency stack
    var isFileChange =
        fileChangeOrPath.getCommitRange && fileChangeOrPath.getPath && fileChangeOrPath.getSrcPath;
    var isFileChangeJSON = fileChangeOrPath.commitRange && fileChangeOrPath.path;
    var fileChangeJSON;

    if (isFileChange || isFileChangeJSON) {
        fileChangeJSON = fileChangeOrPath.toJSON ? fileChangeOrPath.toJSON() : fileChangeOrPath;
    }

    if (fileChangeJSON) {
        var commitRangeJSON = fileChangeJSON.commitRange;
        path = headPath || fileChangeJSON.path;
        path = path.attributes ? path : new Path(path);
        if (commitRangeJSON.pullRequest) {
            builder = builder.pushComponents('pull-requests', commitRangeJSON.pullRequest.id);
        } else {
            var fileChangePath = new Path(fileChangeJSON.path).toString();
            builder = builder.addParams(
                omitUndefined({
                    autoSincePath:
                        autoSincePath === false && !fileChangeJSON.srcPath
                            ? autoSincePath
                            : undefined,
                    since:
                        (commitRangeJSON.sinceRevision && commitRangeJSON.sinceRevision.id) ||
                        undefined,
                    sincePath:
                        (fileChangeJSON.srcPath && new Path(fileChangeJSON.srcPath).toString()) ||
                        undefined,
                    until:
                        (commitRangeJSON.untilRevision && commitRangeJSON.untilRevision.id) ||
                        undefined,
                    untilPath: path.toString() !== fileChangePath ? fileChangePath : undefined,
                })
            );
        }
    } else {
        path = fileChangeOrPath;
    }

    builder = builder.pushComponents('diff'); // need to do this separately otherwise we don't have the correct context for the next apply invocation.
    builder = builder.pushComponents.apply(builder, componentsFromArguments([path]));
    builder = builder.makeBuilder(RevisionRangeSpecifyingBuilderMethods);

    if (headRef && !headRef.isDefault()) {
        builder = builder.at(headRef.getId());
    }

    return builder;
}

/**
 * {@linkcode bitbucket/util/navbuilder.Builder} for commit-related URLs.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class CommitBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * Documented at parent builders
 * @private
 */
function repoCommit(commitId) {
    // commitId must be SHA1 hash
    return this._path()
        .pushComponents('commits', commitId)
        .makeBuilder(
            /**
             * @lends bitbucket/util/navbuilder.CommitBuilder.prototype
             */
            {
                /**
                 * @param {number} commentId - The commit comment to form URLs for.
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                comment: function repoCommitComment(commentId) {
                    return this._path()
                        .addParams({
                            commentId: commentId,
                        })
                        .makeBuilder();
                },
            }
        );
}

/**
 * Documented at parent builders
 * @private
 */
function mergeChecks() {
    return this._path()
        .pushComponents('settings', 'merge-checks')
        .makeBuilder();
}
//--- Pull Request Methods ---

/**
 * {@linkcode bitbucket/util/navbuilder.Builder} for URLs that have a source and target branch.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class SourceTargetBuilder
 * @memberof bitbucket/util/navbuilder
 */

var secondBranchParamBuilders =
    /**
     * @lends bitbucket/util/navbuilder.SourceTargetBuilder.prototype
     */
    {
        /**
         * The builder that will be returned from branch setter methods
         * @private
         */
        _builder: null,
        /**
         * @param {string} sourceBranchRefId - The refId for the source branch.
         * @returns {bitbucket/util/navbuilder.SourceTargetBuilder}
         */
        sourceBranch: function sourceBranch(sourceBranchRefId) {
            return this._path()
                .addParams({ sourceBranch: sourceBranchRefId })
                .makeBuilder(this._builder);
        },
        /**
         * @param {string} targetBranchRefId - The refId for the target branch.
         * @returns {bitbucket/util/navbuilder.SourceTargetBuilder}
         */
        targetBranch: function targetBranch(targetBranchRefId) {
            return this._path()
                .addParams({ targetBranch: targetBranchRefId })
                .makeBuilder(this._builder);
        },
        /**
         * @param {string} id - The ID (not slug) for the target repository (containing the target branch).
         * @returns {bitbucket/util/navbuilder.SourceTargetBuilder}
         */
        targetRepo: function targetRepo(id) {
            return this._path()
                .addParams({ targetRepoId: id })
                .makeBuilder(this._builder);
        },
    };
// set the builder
secondBranchParamBuilders._builder = secondBranchParamBuilders;

var compareDefaultBranchParamBuilders = _.assign(
    {
        /**
         * Documented under Builder
         * @private
         */
        build: function() {
            return this._path()
                .pushComponents('commits')
                .toString(); //the stem /projects/PROJ/repos is different to the path needed if build() is called
        },
    },
    secondBranchParamBuilders
);
// set the builder
compareDefaultBranchParamBuilders._builder = compareDefaultBranchParamBuilders;

/**
 * Pull request-related {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class PullRequestBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * Documented at parent builder
 * @private
 */
function pullRequest(prOrId) {
    var changeBuilder =
        /**
         * @lends bitbucket/util/navbuilder.PullRequestDiffBuilder.prototype
         */
        {
            /**
             * @param {string} diffChangePath - The filepath to view the change of, within the PR.
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            change: function(diffChangePath) {
                return this._path()
                    .withFragment(diffChangePath)
                    .makeBuilder();
            },
        };

    return this._path()
        .pushComponents('pull-requests', getPullRequestId(prOrId))
        .makeBuilder(
            /**
             * @lends bitbucket/util/navbuilder.PullRequestBuilder.prototype
             */
            {
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                unwatch: function pullRequestUnwatch() {
                    return this._path()
                        .pushComponents('unwatch')
                        .makeBuilder();
                },
                /**
                 * @param {string} [commitId] - The ID of a commit in the pull request.
                 * @returns {bitbucket/util/navbuilder.PullRequestDiffBuilder}
                 */
                commit: function pullRequestCommit(commitId) {
                    //Unlike repository commits, ref names like "master" are not supported here. As a result, there is
                    //no need to do all the path gyrations repoCommit does
                    return this._path()
                        .pushComponents('commits', commitId)
                        .makeBuilder(
                            /**
                             * @lends bitbucket/util/navbuilder.PullRequestDiffBuilder.prototype
                             */
                            {
                                /**
                                 * @param {number} commentId - The ID of a comment to view within the overview activity.
                                 * @returns {bitbucket/util/navbuilder.Builder}
                                 */
                                since: function pullRequestCommitRange(sinceId) {
                                    return this._path()
                                        .addParams({ since: sinceId })
                                        .makeBuilder(changeBuilder);
                                },
                                ...changeBuilder,
                            }
                        );
                },
                /**
                 * @returns {bitbucket/util/navbuilder.PullRequestOverviewBuilder}
                 */
                overview: function pullRequestOverview() {
                    /**
                     * Pull request overview URL {@linkcode bitbucket/util/navbuilder.Builder}.
                     *
                     * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                     *
                     * @class PullRequestOverviewBuilder
                     * @memberof bitbucket/util/navbuilder
                     */
                    return this._path()
                        .pushComponents('overview')
                        .makeBuilder(
                            /**
                             * @lends bitbucket/util/navbuilder.PullRequestOverviewBuilder.prototype
                             */
                            {
                                /**
                                 * @param {number} commentId - The ID of a comment to view within the overview activity.
                                 * @returns {bitbucket/util/navbuilder.Builder}
                                 */
                                comment: function pullRequestComment(commentId) {
                                    return this._path()
                                        .addParams({ commentId: commentId })
                                        .makeBuilder();
                                },

                                /**
                                 * @param {number} activityId - The ID of an activity to view within the overview activity.
                                 * @returns {bitbucket/util/navbuilder.Builder}
                                 */
                                activity: function pullRequestActivity(activityId) {
                                    return this._path()
                                        .addParams({ activityId: activityId })
                                        .makeBuilder();
                                },
                            }
                        );
                },
                /**
                 * @returns {bitbucket/util/navbuilder.PullRequestDiffBuilder}
                 */
                diff: function pullRequestDiff() {
                    /**
                     * Pull request diff-related {@linkcode bitbucket/util/navbuilder.Builder}.
                     *
                     * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                     *
                     * @class PullRequestDiffBuilder
                     * @memberof bitbucket/util/navbuilder
                     */
                    return this._path()
                        .pushComponents('diff')
                        .makeBuilder(changeBuilder);
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                unreviewed: function pullRequestUnreviewed() {
                    /**
                     * Same as {@linkcode bitbucket/util/navbuilder.PullRequestDiffBuilder}.
                     */
                    return this._path()
                        .pushComponents('unreviewed')
                        .makeBuilder();
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                commits: function pullRequestCommits() {
                    return this._path()
                        .pushComponents('commits')
                        .makeBuilder();
                },
                /**
                 * Documented on Builder
                 * @private
                 */
                build: function() {
                    return this._path()
                        .pushComponents('overview')
                        .toString(); //Default to overview view
                },
                /**
                 * @private
                 */
                buildBase: function() {
                    return this._path().toString();
                },
            }
        );
}

/**
 * Returns the builder for the current page's pull request, if there is one.
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.PullRequestBuilder}
 */
function currentPullRequest() {
    return currentRepo.call(this).pullRequest(getCurrentPullRequest());
}

/**
 * REST project-related {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class RestProjectBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * Documented at parent builder
 * @private
 */
function restProj(projectOrKey) {
    var key = getProjectKey(projectOrKey);

    return this._path()
        .pushComponents('projects', key)
        .makeBuilder(
            /**
             * @lends bitbucket/util/navbuilder.RestProjectBuilder.prototype
             */
            {
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                allRepos: function restAllRepos() {
                    return this._path()
                        .pushComponents('repos')
                        .makeBuilder();
                },
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                hooks: restHooks,
                /**
                 * @method
                 * @param {string} hookKey - The key of the hook to form URLs about.
                 * @returns {bitbucket/util/navbuilder.RestHookBuilder}
                 */
                hook: restHook,
                /**
                 * @method
                 * @param {string} slug - The slug of the repo to form URLs for.
                 * @returns {bitbucket/util/navbuilder.RestRepositoryBuilder}
                 */
                repo: restRepo,
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.RestProjectPermissionsBuilder}
                 */
                permissions: restProjPermissions,
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                pullRequestSettings: function pullRequestSettings() {
                    return this._path()
                        .pushComponents('settings')
                        .pushComponents('pull-requests')
                        .pushComponents('git')
                        .makeBuilder();
                },
            }
        );
}

/**
 * Documented at parent builder
 * @private
 */
function restCurrentProject() {
    return restProj.call(this, getCurrentProject());
}

/**
 * Documented at parent builder
 * @private
 */
function restCurrentRepo() {
    return restCurrentProject.call(this).repo(getCurrentRepository());
}

/**
 * Documented at parent builders
 * @private
 */
function restHooks() {
    return this._path()
        .pushComponents('settings', 'hooks')
        .makeBuilder();
}

/**
 * REST repository hook-related {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class RestHookBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * @param {string} hookKey - The key of the hook you want to build URLs for.
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.RestHookBuilder}
 */
function restHook(hookOrKey) {
    var hookKey = getHookKey(hookOrKey);

    return this._path()
        .pushComponents('settings')
        .pushComponents('hooks', hookKey)
        .makeBuilder(
            /**
             * @lends bitbucket/util/navbuilder.RestHookBuilder.prototype
             */
            {
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                enabled: function hookEnabled() {
                    return this._path()
                        .pushComponents('enabled')
                        .makeBuilder();
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                settings: function hookSettings() {
                    return this._path()
                        .pushComponents('settings')
                        .makeBuilder();
                },
            }
        );
}

/**
 * REST repository-related {@linkcode bitbucket/util/navbuilder.Builder}.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class RestRepositoryBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * Documented at parent builders
 * @private
 */
function restRepo(repoOrSlug) {
    var slug = getRepoSlug(repoOrSlug);

    return this._path()
        .pushComponents('repos', slug)
        .makeBuilder(
            /**
             * @lends bitbucket/util/navbuilder.RestRepositoryBuilder.prototype
             */
            {
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                tags: function restRepoTags() {
                    return this._path()
                        .pushComponents('tags')
                        .makeBuilder();
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                branches: function restRepoBranches() {
                    return this._path()
                        .pushComponents('branches')
                        .makeBuilder();
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                commits: function restRepoAllCommits() {
                    return this._path()
                        .pushComponents('commits')
                        .makeBuilder({
                            /**
                             * @param {string} [since] - The ID of the ref to use as the source of the commit list.
                             * @memberof bitbucket/util/navbuilder
                             * @returns {bitbucket/util/navbuilder.Builder}
                             */
                            since: function repoCommitsSince(since) {
                                var builder = this._path();

                                if (since) {
                                    if (typeof since === 'string') {
                                        // if it's a new branch the since hash will be 0000000000000000000000000000000000000000
                                        // filter out these requests and do not send the since parameter
                                        if (since === '0000000000000000000000000000000000000000') {
                                            return builder.makeBuilder();
                                        }
                                    } else {
                                        since = since.id;
                                    }
                                    builder = builder.addParams({
                                        since: since,
                                    });
                                }

                                return builder.makeBuilder();
                            },
                        });
                },
                /**
                 * @method
                 * @param {string|JSON.CommitRangeJSON} commitIdOrCommitRange
                 * @returns {bitbucket/util/navbuilder.RestCommitBuilder}
                 */
                commit: restRepoCommit,
                /**
                 * @returns {bitbucket/util/navbuilder.RestRepoCompareBuilder}
                 */
                compare: function restRepoCompare() {
                    /**
                     * REST ref comparison-related {@linkcode bitbucket/util/navbuilder.Builder}.
                     *
                     * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                     *
                     * @class RestRepoCompareBuilder
                     * @memberof bitbucket/util/navbuilder
                     */

                    var compareParam = function(name, value) {
                        var params = {};
                        params[name] = value;

                        return this._path()
                            .addParams(params)
                            .makeBuilder(paramsBuilder);
                    };

                    var comparePath = function(path) {
                        return function() {
                            return this._path()
                                .pushComponents(path)
                                .makeBuilder(paramsBuilder);
                        };
                    };

                    /**
                     * {@linkcode bitbucket/util/navbuilder.Builder} for REST ref comparison-related params.
                     *
                     * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                     *
                     * @class RestRepoCompareParamBuilder
                     * @memberof bitbucket/util/navbuilder
                     */

                    var paramsBuilder =
                        /**
                         * @lends bitbucket/util/navbuilder.RestRepoCompareParamBuilder.prototype
                         */
                        {
                            /**
                             * @method
                             * @param {string} refId - The from/until ref's ID for comparison.
                             * @returns {bitbucket/util/navbuilder.RestRepoCompareParamBuilder}
                             */
                            from: _.partial(compareParam, 'from'),
                            /**
                             * @method
                             * @param {string} refId - The to/since ref's ID for comparison.
                             * @returns {bitbucket/util/navbuilder.RestRepoCompareParamBuilder}
                             */
                            to: _.partial(compareParam, 'to'),
                            /**
                             * @method
                             * @param {string} repoId - The ID of the repository containing the from/until ref.
                             * @returns {bitbucket/util/navbuilder.RestRepoCompareParamBuilder}
                             */
                            fromRepo: _.partial(compareParam, 'fromRepo'),
                        };

                    return this._path()
                        .pushComponents('compare')
                        .makeBuilder(
                            /**
                             * @lends bitbucket/util/navbuilder.RestRepoCompareBuilder.prototype
                             */
                            {
                                /**
                                 * @method
                                 * @returns {bitbucket/util/navbuilder.RestRepoCompareParamBuilder}
                                 */
                                changes: comparePath('changes'),
                                /**
                                 * @method
                                 * @returns {bitbucket/util/navbuilder.RestRepoCompareParamBuilder}
                                 */
                                commits: comparePath('commits'),
                                /**
                                 * @method
                                 * @param {JSON.FileChangeJSON} fileChange - The fileChange object describing the files to be compared.
                                 * @returns {bitbucket/util/navbuilder.RestRepoCompareParamBuilder}
                                 */
                                diff: function(fileChange) {
                                    return restDiffInternal.call(this, fileChange, paramsBuilder);
                                },
                            }
                        );
                },
                /**
                 * @param {JSON.CommitRangeJSON} commitRange - describe the range of commits to get changes between.
                 * @returns {bitbucket/util/navbuilder.RestCommitPathBuilder|bitbucket/util/navbuilder.Builder}
                 */
                changes: function routeChangesRequest(commitRange) {
                    commitRange = commitRange.toJSON ? commitRange.toJSON() : commitRange;
                    if (commitRange.pullRequest) {
                        const prChangesBuilder = this.pullRequest(
                            commitRange.pullRequest
                        ).changes();

                        if (commitRange.diffType === EFFECTIVE) {
                            // Eventually we'll want to make use of the since/until IDs, but currently
                            // they wrongly point to the branch heads.
                            return prChangesBuilder;
                        }

                        const params = {
                            changeScope: 'RANGE',
                            untilId: commitRange.untilRevision.id,
                        };

                        if (commitRange.sinceRevision && commitRange.sinceRevision.id) {
                            params.sinceId = commitRange.sinceRevision.id;
                        }

                        return prChangesBuilder.withParams(params);
                    } else if (commitRange.untilRevision) {
                        return this.commit(commitRange).changes();
                    }
                    throw new Error('A valid commit-range is required to retrieve changes');
                },
                /**
                 * @returns {bitbucket/util/navbuilder.RepoBrowseBuilder}
                 */
                browse: function restRepoBrowse() {
                    return this._path()
                        .pushComponents('browse')
                        .makeBuilder(RepoBrowseBuilderMethods);
                },
                /**
                 * @returns {bitbucket/util/navbuilder.RepoBrowseBuilder}
                 */
                raw: function restRepoRaw() {
                    //The parameters are exactly the same as for the browse builder, except the endpoint is /raw/
                    return this._path()
                        .pushComponents('raw')
                        .makeBuilder(RepoBrowseBuilderMethods);
                },
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.RestRepoFilesListBuilder}
                 */
                files: restRepoFind,
                /**
                 *  @returns {bitbucket/util/navbuilder.Builder}
                 */
                forks: function restRepoForks() {
                    return this._path()
                        .pushComponents('forks')
                        .makeBuilder();
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                related: function restRepoRelated() {
                    return this._path()
                        .pushComponents('related')
                        .makeBuilder();
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                participants: function restRepoParticipants() {
                    return this._path()
                        .pushComponents('participants')
                        .makeBuilder();
                },
                /**
                 * @method
                 * @param {number} id - The ID of a pull request.
                 * @returns {bitbucket/util/navbuilder.RestPullRequestBuilder}
                 */
                pullRequest: restPullRequest,
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                allPullRequests: function restAllPullRequests() {
                    return this._path()
                        .pushComponents('pull-requests')
                        .makeBuilder();
                },
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                hooks: restHooks,
                /**
                 * @method
                 * @param {string} hookKey - The key of the hook to form URLs about.
                 * @returns {bitbucket/util/navbuilder.RestHookBuilder}
                 */
                hook: restHook,
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.RepoBrowseBuilder}
                 */
                lastModified: function lastModified() {
                    return this._path()
                        .pushComponents('last-modified')
                        .makeBuilder(RepoBrowseBuilderMethods);
                },
                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                pullRequestSettings: function pullRequestSettings() {
                    return this._path()
                        .pushComponents('settings')
                        .pushComponents('pull-requests')
                        .makeBuilder();
                },

                /**
                 * @method
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                webhooks: function webhookResource() {
                    return this._path()
                        .pushComponents('webhooks')
                        .makeBuilder({
                            id: function(id) {
                                return this._path()
                                    .pushComponents(id)
                                    .makeBuilder();
                            },
                        });
                },
            }
        );
}

/**
 * {@linkcode bitbucket/util/navbuilder.Builder} for REST pull request info.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class RestPullRequestBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * Documented at parent builders
 * @private
 */
function restPullRequest(prOrId) {
    var id = getPullRequestId(prOrId);

    return this._path()
        .pushComponents('pull-requests', id)
        .makeBuilder(
            /**
             * @lends bitbucket/util/navbuilder.RestPullRequestBuilder.prototype
             */
            {
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                activities: function() {
                    return this._path()
                        .pushComponents('activities')
                        .makeBuilder();
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                approve: function restApprove() {
                    return this._path()
                        .pushComponents('approve')
                        .makeBuilder();
                },
                /**
                 * @param {number} id - The ID of the comment to generate URLs for.
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                comment: function restComment(id) {
                    return this._path()
                        .pushComponents('comments', id)
                        .makeBuilder({
                            comment: restComment /* TODO is this legit?!?!?! Not documenting it for now... */,
                        });
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                comments: function() {
                    return this._path()
                        .addParams({
                            diffType: EFFECTIVE,
                        })
                        .pushComponents('comments')
                        .makeBuilder({
                            commit(c) {
                                return this.withParams({
                                    diffType: COMMIT,
                                    toHash: c.getId ? c.getId() : c.id || c,
                                });
                            },
                            range(since, until) {
                                return this.withParams({
                                    diffType: RANGE,
                                    toHash: until,
                                    fromHash: since,
                                });
                            },
                        });
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                commits: function() {
                    return this._path()
                        .pushComponents('commits')
                        .makeBuilder();
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                changes: function restPullRequestChanges() {
                    return this._path()
                        .pushComponents('changes')
                        .makeBuilder();
                },
                /**
                 * @method
                 * @param {JSON.FileChangeJSON} fileChange
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                diff: restDiff,
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                watch: function restPullRequestWatch() {
                    return this._path()
                        .pushComponents('watch')
                        .makeBuilder();
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                merge: function restMerge() {
                    return this._path()
                        .pushComponents('merge')
                        .makeBuilder();
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                participants: function restParticipants(user) {
                    if (user) {
                        return this._path()
                            .pushComponents('participants')
                            .pushComponents(user.slug)
                            .makeBuilder();
                    }

                    return this._path()
                        .pushComponents('participants')
                        .makeBuilder();
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                reopen: function restReopen() {
                    return this._path()
                        .pushComponents('reopen')
                        .makeBuilder();
                },
                /**
                 * @returns {bitbucket/util/navbuilder.Builder}
                 */
                decline: function restDecline() {
                    return this._path()
                        .pushComponents('decline')
                        .makeBuilder();
                },
            }
        );
}

/**
 * Documented at parent builders
 * @private
 */
function restCurrentPullRequest() {
    return restCurrentRepo.call(this).pullRequest(getCurrentPullRequest());
}

/**
 * {@linkcode bitbucket/util/navbuilder.Builder} for REST commit URLs.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class RestCommitBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * Documented at parent builders
 * @private
 */
function restRepoCommit(commitIdOrCommitRange) {
    commitIdOrCommitRange = commitIdOrCommitRange.toJSON
        ? commitIdOrCommitRange.toJSON()
        : commitIdOrCommitRange;

    var path = this._path().pushComponents('commits');

    if (typeof commitIdOrCommitRange === 'string') {
        path = path.pushComponents(commitIdOrCommitRange);
    } else if (commitIdOrCommitRange.untilRevision) {
        path = path.pushComponents(commitIdOrCommitRange.untilRevision.id);

        var sinceId = commitIdOrCommitRange.sinceRevision && commitIdOrCommitRange.sinceRevision.id;

        if (sinceId) {
            path = path.addParams({ since: sinceId });
        }
    } else {
        throw new Error(AJS.I18n.getText('bitbucket.web.error.no.commit.or.commitRange'));
    }

    return path.makeBuilder(
        /**
         * @lends bitbucket/util/navbuilder.RestCommitBuilder.prototype
         */
        {
            /**
             * @method
             * @param {JSON.FileChangeJSON} fileChange
             * @returns {bitbucket/util/navbuilder.Builder}
             */
            diff: restDiff,
            /**
             * @returns {bitbucket/util/navbuilder.RestCommitPathBuilder}
             */
            changes: function restCommitChanges() {
                return this._path()
                    .pushComponents('changes')
                    .makeBuilder(restCommitPathBuilderMethods);
            },
            /**
             * @returns {bitbucket/util/navbuilder.RestCommitPathBuilder}
             */
            comments: function restCommitComment() {
                return this._path()
                    .pushComponents('comments')
                    .makeBuilder(restCommitPathBuilderMethods);
            },
            /**
             * @returns {bitbucket/util/navbuilder.RepoBrowseBuilder}
             */
            watch: function restCommitWatch() {
                return this._path()
                    .pushComponents('watch')
                    .makeBuilder();
            },
            /**
             * @returns {bitbucket/util/navbuilder.RepoBrowseBuilder}
             */
            pullRequests: function restCommitPullRequests() {
                return this._path()
                    .pushComponents('pull-requests')
                    .makeBuilder();
            },
        }
    );
}

/**
 * {@linkcode bitbucket/util/navbuilder.Builder} for REST commit path URLs.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class RestCommitPathBuilder
 * @memberof bitbucket/util/navbuilder
 */
var restCommitPathBuilderMethods =
    /**
     * @lends bitbucket/util/navbuilder.RestCommitPathBuilder.prototype
     */
    {
        /**
         * @param {string} path - A file path.
         * @returns {bitbucket/util/navbuilder.Builder}
         */
        path: function restCommitPath(path) {
            return this._path()
                .addParams({
                    path: path.toString(),
                })
                .makeBuilder();
        },
    };

/**
 * @private
 */
function restDiffInternal(fileChange, builderMethods) {
    var fileChangeJSON = fileChange.toJSON ? fileChange.toJSON() : fileChange;

    var builder = this._path();

    builder = builder.pushComponents('diff');
    builder = builder.pushComponents.apply(builder, componentsFromArguments([fileChangeJSON.path]));

    if (fileChangeJSON.srcPath) {
        builder = builder.addParams({
            srcPath: new Path(fileChangeJSON.srcPath).toString(),
        });
    }

    return builder.makeBuilder(builderMethods);
}

/**
 * Documented at parent builders
 * @private
 */
function restDiff(fileChange) {
    return restDiffInternal.call(this, fileChange);
}

/**
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.RepoBrowseBuilder}
 */
function restRepoBrowse() {
    return this._path()
        .pushComponents('browse')
        .makeBuilder(RepoBrowseBuilderMethods);
}

/**
 * @memberof bitbucket/util/navbuilder
 * @returns {bitbucket/util/navbuilder.RepoBrowseBuilder}
 */
function restRawBrowse() {
    return this._path()
        .pushComponents('raw')
        .makeBuilder(RepoBrowseBuilderMethods);
}

/**
 * {@linkcode bitbucket/util/navbuilder.Builder} for specifying revisions in REST repository files list URLs.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class RestRepoFilesListAtBuilder
 * @memberof bitbucket/util/navbuilder
 */
var restRepoFilesListAtBuilderMethods =
    /**
     * @lends bitbucket/util/navbuilder.RestRepoFilesListAtBuilder.prototype
     */
    {
        /**
         * Set the "at" revision at which to obtain a file list from the repo.
         *
         * @method
         * @param {string} refId - The ID of a ref or commit in the SCM whose history to browse.
         * @returns {bitbucket/util/navbuilder.Builder}
         */
        at: RevisionRangeSpecifyingBuilderMethods.at,
    };

/**
 * {@linkcode bitbucket/util/navbuilder.Builder} for REST repository files list URLs.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class RestRepoFilesListBuilder
 * @extends bitbucket/util/navbuilder.RestRepoFilesListAtBuilder
 * @memberof bitbucket/util/navbuilder
 */
var restRepoFilesListBuilderMethods =
    /**
     * @lends bitbucket/util/navbuilder.RestRepoFilesListBuilder.prototype
     */
    {
        /**
         * @param {...string} component - A path component within which to obtain a list of files.
         * @returns {bitbucket/util/navbuilder.RestRepoFilesListAtBuilder}
         */
        path: function restRepoFilesInPath() {
            var path = this._path();

            return path.pushComponents
                .apply(path, componentsFromArguments(arguments))
                .makeBuilder(restRepoFilesListAtBuilderMethods);
        },
        at: restRepoFilesListAtBuilderMethods.at,
    };

/**
 * Documented at parent builder
 * @private
 */
function restRepoFind() {
    return this._path()
        .pushComponents('files')
        .makeBuilder(restRepoFilesListBuilderMethods);
}

/**
 * {@linkcode bitbucket/util/navbuilder.Builder} for REST project permissions URLs.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class RestProjectPermissionsBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * Documented at parent builder
 * @private
 */
function restProjPermissions() {
    return this._path()
        .pushComponents('permissions')
        .makeBuilder(
            /**
             * @lends bitbucket/util/navbuilder.RestProjectPermissionsBuilder.prototype
             */
            {
                /**
                 * {@linkcode bitbucket/util/navbuilder.Builder} for REST project read permissions URLs.
                 *
                 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                 *
                 * @class RestProjectReadPermissionsBuilder
                 * @memberof bitbucket/util/navbuilder
                 */
                /**
                 * @returns {bitbucket/util/navbuilder.RestProjectReadPermissionsBuilder}
                 */
                projectRead: function restProjReadPerms() {
                    return this._path()
                        .pushComponents('project-read')
                        .makeBuilder(
                            /**
                             * @lends bitbucket/util/navbuilder.RestProjectReadPermissionsBuilder.prototype
                             */
                            {
                                /**
                                 * @method
                                 * @returns {bitbucket/util/navbuilder.RestPermissionAllowBuilder}
                                 */
                                all: restAllProjPerms,
                                /**
                                 * @returns {bitbucket/util/navbuilder.RestPermissionAllowBuilder}
                                 */
                                anon: function restAnonProReadPerms() {
                                    return this._path()
                                        .pushComponents('anon')
                                        .makeBuilder(allowBuilderMethods);
                                },
                            }
                        );
                },
                /**
                 * {@linkcode bitbucket/util/navbuilder.Builder} for REST project read permissions URLs.
                 *
                 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                 *
                 * @class RestProjectWritePermissionsBuilder
                 * @memberof bitbucket/util/navbuilder
                 */
                /**
                 * @returns {bitbucket/util/navbuilder.RestProjectWritePermissionsBuilder}
                 */
                projectWrite: function restProjWritePerms() {
                    return this._path()
                        .pushComponents('project-write')
                        .makeBuilder(
                            /**
                             * @lends bitbucket/util/navbuilder.RestProjectWritePermissionsBuilder.prototype
                             */
                            {
                                /**
                                 * @method
                                 * @returns {bitbucket/util/navbuilder.RestPermissionAllowBuilder}
                                 */
                                all: restAllProjPerms,
                            }
                        );
                },
            }
        );
}

/**
 * {@linkcode bitbucket/util/navbuilder.Builder} to grant permissions.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class RestPermissionAllowBuilder
 * @memberof bitbucket/util/navbuilder
 */
var allowBuilderMethods =
    /**
     * @lends bitbucket/util/navbuilder.RestPermissionAllowBuilder.prototype
     */
    {
        /**
         * @method
         * @returns {bitbucket/util/navbuilder.Builder}
         */
        allow: restAllowProjPerms,
    };

/**
 * Documented at parent builders
 * @private
 */
function restAllProjPerms() {
    return this._path()
        .pushComponents('all')
        .makeBuilder(allowBuilderMethods);
}

/**
 * Documented at parent builders
 * @private
 */
function restAllowProjPerms(allow) {
    return this._path()
        .addParams({ allow: allow })
        .makeBuilder();
}

/**
 * {@linkcode bitbucket/util/navbuilder.Builder} for info about repository hook plugins.
 *
 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
 *
 * @class RestHookPluginsBuilder
 * @memberof bitbucket/util/navbuilder
 */

/**
 * Documented at parent builders
 * @private
 */
function restHookPlugins() {
    return this._path()
        .pushComponents('hooks')
        .makeBuilder(
            /**
             * @lends bitbucket/util/navbuilder.RestHookPluginsBuilder.prototype
             */
            {
                /**
                 * {@linkcode bitbucket/util/navbuilder.Builder} for info about a repository hook plugin.
                 *
                 * NOTE: The constructor is not exposed. A new instance can be created through the Builder API.
                 *
                 * @class RestHookPluginBuilder
                 * @memberof bitbucket/util/navbuilder
                 */

                /**
                 * @param {string} hookKey - The key of the hook.
                 * @returns {bitbucket/util/navbuilder.RestHookPluginBuilder}
                 */
                hook: function restHookPlugin(hookKey) {
                    return this._path()
                        .pushComponents(hookKey)
                        .makeBuilder(
                            /**
                             * @lends bitbucket/util/navbuilder.RestHookPluginBuilder.prototype
                             */
                            {
                                /**
                                 * @param {string} [version] - A string identifying the version of the hook plugin.
                                 *                             Used only to invalidate browser caches when the plugin updates.
                                 * @returns {bitbucket/util/navbuilder.Builder}
                                 */
                                avatar: function restHookAvatar(version) {
                                    return this._path()
                                        .pushComponents('avatar')
                                        .addParams({ version: version })
                                        .makeBuilder();
                                },
                            }
                        );
                },
            }
        );
}

/**
 * Documented at parent builders.
 * @private
 */
function userAvatar(size) {
    var builder = this._path().pushComponents('avatar.png');

    if (size) {
        builder = builder.addParams({ s: size });
    }

    return builder.makeBuilder();
}

// HACKY CODE CHECK: off
var fallbackUrlPattern = /(default-avatar-)\d+(\.png)/;

/**
 * @private
 */
function _avatarUrl(entity, size) {
    return {
        build: function() {
            var uri = parse(entity.avatarUrl);

            if (uri.getQueryParamValue('s')) {
                uri.replaceQueryParam('s', size);
            }

            // If what we're looking at is the default avatar, its size is set differently,
            // so use a regex in the filename, rather than a query string param, to insert the correct size.
            return uri.toString().replace(fallbackUrlPattern, '$1' + size + '$2');
        },
    };
}

// HACKY CODE CHECK: on

/**
 * An object representation of a URI with methods allowing you to read and modify the URI.
 *
 * NOTE: The Uri constructor is not exposed. Use {@linkcode bitbucket/util/navbuilder.parse} to create an instance.
 *
 * @class Uri
 * @memberof bitbucket/util/navbuilder
 */

/**
 * Serialize the Uri instance.
 *
 * @function toString
 * @name bitbucket/util/navbuilder.Uri#toString
 * @returns {string}
 */

/**
 * Get just the path portion of the URI.
 *
 * @function path
 * @name bitbucket/util/navbuilder.Uri#path
 * @returns {string}
 */

/**
 * Get just the query string portion of the URI, as an object.
 *
 * @function query
 * @name bitbucket/util/navbuilder.Uri#query
 * @returns {bitbucket/util/navbuilder.Query}
 */

/**
 * Get just the anchor/hash portion of the URI.
 *
 * @function anchor
 * @name bitbucket/util/navbuilder.Uri#anchor
 * @returns {string}
 */

/**
 * Add a query parameter value. If the key is already present in the URI, the key will be repeated.
 *
 * @function addQueryParam
 * @name bitbucket/util/navbuilder.Uri#addQueryParam
 * @param {string} paramKey - The key/name of the parameter.
 * @param {string} paramValue - The value for the parameter.
 * @returns {bitbucket/util/navbuilder.Uri}
 */

/**
 * Add a query parameter value. If the key is already present in the URI, it will be replaced.
 *
 * @function replaceQueryParam
 * @name bitbucket/util/navbuilder.Uri#replaceQueryParam
 * @param {string} paramKey - The key/name of the parameter.
 * @param {string} paramValue - The value for the parameter.
 * @returns {bitbucket/util/navbuilder.Uri}
 */

/**
 * Remove a query parameter from the URI.
 *
 * @function deleteQueryParam
 * @name bitbucket/util/navbuilder.Uri#deleteQueryParam
 * @param {string} paramKey - The key/name of the parameter.
 * @returns {bitbucket/util/navbuilder.Uri}
 */

/**
 * Get a query parameter value. If the key is repeated in the URI, the first instance will be returned.
 *
 * @function getQueryParamValue
 * @name bitbucket/util/navbuilder.Uri#getQueryParamValue
 * @param {string} paramKey - The key/name of the parameter.
 * @returns {string}
 */

/**
 * Get an array of query parameter values for a key.
 *
 * @function getQueryParamValues
 * @name bitbucket/util/navbuilder.Uri#getQueryParamValues
 * @param {string} paramKey - The key/name of the parameter.
 * @returns {Array<string>}
 */

/**
 * Parse an absolute string URI in to a mutable Uri object.
 * @memberof bitbucket/util/navbuilder
 * @param {string} uri - An absolute URI to be parsed.
 * @returns {bitbucket/util/navbuilder.Uri}
 */
function parse(uri) {
    return new Uri(uri);
}

/**
 * An object representation of a query string with methods allowing you to read and modify the string.
 *
 * NOTE: The Query constructor is not exposed. Use {@linkcode bitbucket/util/navbuilder.parseQuery} to create an instance.
 *
 * @class Query
 * @memberof bitbucket/util/navbuilder
 */

/**
 * Serialize the Query instance.
 *
 * @function toString
 * @name bitbucket/util/navbuilder.Query#toString
 * @returns {string}
 */

/**
 * Add a query parameter value. If the key is already present in the Query, the key will be repeated.
 *
 * @function addParam
 * @name bitbucket/util/navbuilder.Query#addParam
 * @param {string} paramKey - The key/name of the parameter.
 * @param {string} paramValue - The value for the parameter.
 * @returns {bitbucket/util/navbuilder.Query}
 */

/**
 * Add a query parameter value. If the key is already present in the Query, it will be replaced.
 *
 * @function replaceParam
 * @name bitbucket/util/navbuilder.Query#replaceParam
 * @param {string} paramKey - The key/name of the parameter.
 * @param {string} paramValue - The value for the parameter.
 * @returns {bitbucket/util/navbuilder.Query}
 */

/**
 * Remove a query parameter from the URI.
 *
 * @function deleteParam
 * @name bitbucket/util/navbuilder.Query#deleteParam
 * @param {string} paramKey - The key/name of the parameter.
 * @returns {bitbucket/util/navbuilder.Query}
 */

/**
 * Get a query parameter value. If the key is repeated in the Query, the first instance will be returned.
 *
 * @function getParamValue
 * @name bitbucket/util/navbuilder.Query#getParamValue
 * @param {string} paramKey - The key/name of the parameter.
 * @returns {string}
 */

/**
 * Get an array of query parameter values for a key.
 *
 * @function getParamValues
 * @name bitbucket/util/navbuilder.Query#getParamValues
 * @param {string} paramKey - The key/name of the parameter.
 * @returns {Array<string>}
 */

/**
 * Parse a URI's query string into a mutable object.
 * @memberof bitbucket/util/navbuilder
 * @param {string} queryString - A query string to be parsed.
 * @returns {bitbucket/util/navbuilder.Query}
 */
function parseQuery(queryString) {
    /* global Query: false */
    return new Query(queryString);
}

/**
 * Get a raw builder instance to form your own URLs.
 *
 * @memberof bitbucket/util/navbuilder
 * @param {Array<string>} components - An array of path components that will be URI encoded and joined by forward slashes when the URL is formed.
 * @param {Object} params - A map of parameter names to values that will form the query string.
 * @returns {bitbucket/util/navbuilder.Builder}
 */
function newBuilder(components, params) {
    return new PathAndQuery(components, params).makeBuilder();
}

export default {
    allRepos: globalAllRepos,
    _avatarUrl,
    addons,
    admin,
    allProjects,
    captcha,
    createProject,
    currentProject,
    currentPullRequest,
    currentRepo,
    dashboard,
    login,
    newBuilder,
    parse,
    parseQuery,
    pluginServlets,
    project,
    pullRequest: pullRequestShorthand,
    repository: repositoryShorthand,
    rest,
    search,
    tmp,
    user,
    welcome,
    gettingStarted,
};