whole_func_string: function (opts) {
      opts = opts || {};
      const defaults = {
          // the default 'method' is 'GET', as set by 'jquery.ajax'
  
          girderToken: getCurrentToken() || cookie.find('girderToken'),
  
          error: (error, status) => {
              let info;
              if (error.status === 401) {
                  events.trigger('g:loginUi');
                  info = {
                      text: 'You must log in to view this resource',
                      type: 'warning',
                      timeout: 4000,
                      icon: 'info'
                  };
              } else if (error.status === 403) {
                  info = {
                      text: 'Access denied. See the console for more details.',
                      type: 'danger',
                      timeout: 5000,
                      icon: 'attention'
                  };
              } else if (error.status === 0 && error.statusText === 'abort') {
                  /* We expected this abort, so do nothing. */
                  return;
              } else if (error.status === 500 && error.responseJSON &&
                  error.responseJSON.type === 'girder') {
                  info = {
                      text: error.responseJSON.message,
                      type: 'warning',
                      timeout: 5000,
                      icon: 'info'
                  };
              } else if (status === 'parsererror') {
                  info = {
                      text: 'A parser error occurred while communicating with the ' +
                            'server (did you use the correct value for `dataType`?). ' +
                            'Details have been logged in the console.',
                      type: 'danger',
                      timeout: 5000,
                      icon: 'attention'
                  };
              } else {
                  info = {
                      text: 'An error occurred while communicating with the ' +
                            'server. Details have been logged in the console.',
                      type: 'danger',
                      timeout: 5000,
                      icon: 'attention'
                  };
              }
              events.trigger('g:alert', info);
              console.error(error.status + ' ' + error.statusText, error.responseText);
          }
      };
  
      // Overwrite defaults with passed opts, but do not mutate opts
      const args = _.extend({}, defaults, opts);
  
      if (!args.url) {
          throw new Error('restRequest requires a "url" argument');
      }
      args.url = `${getApiRoot()}${args.url.substring(0, 1) === '/' ? '' : '/'}${args.url}`;
  
      if (args.girderToken) {
          args.headers = args.headers || {};
          args.headers['Girder-Token'] = args.girderToken;
          delete args.girderToken;
      }
  
      return Backbone.$.ajax(args);
  }
func_code_string: function (opts) {
      opts = opts || {};
      const defaults = {
          // the default 'method' is 'GET', as set by 'jquery.ajax'
  
          girderToken: getCurrentToken() || cookie.find('girderToken'),
  
          error: (error, status) => {
              let info;
              if (error.status === 401) {
                  events.trigger('g:loginUi');
                  info = {
                      text: 'You must log in to view this resource',
                      type: 'warning',
                      timeout: 4000,
                      icon: 'info'
                  };
              } else if (error.status === 403) {
                  info = {
                      text: 'Access denied. See the console for more details.',
                      type: 'danger',
                      timeout: 5000,
                      icon: 'attention'
                  };
              } else if (error.status === 0 && error.statusText === 'abort') {
                  /* We expected this abort, so do nothing. */
                  return;
              } else if (error.status === 500 && error.responseJSON &&
                  error.responseJSON.type === 'girder') {
                  info = {
                      text: error.responseJSON.message,
                      type: 'warning',
                      timeout: 5000,
                      icon: 'info'
                  };
              } else if (status === 'parsererror') {
                  info = {
                      text: 'A parser error occurred while communicating with the ' +
                            'server (did you use the correct value for `dataType`?). ' +
                            'Details have been logged in the console.',
                      type: 'danger',
                      timeout: 5000,
                      icon: 'attention'
                  };
              } else {
                  info = {
                      text: 'An error occurred while communicating with the ' +
                            'server. Details have been logged in the console.',
                      type: 'danger',
                      timeout: 5000,
                      icon: 'attention'
                  };
              }
              events.trigger('g:alert', info);
              console.error(error.status + ' ' + error.statusText, error.responseText);
          }
      };
  
      // Overwrite defaults with passed opts, but do not mutate opts
      const args = _.extend({}, defaults, opts);
  
      if (!args.url) {
          throw new Error('restRequest requires a "url" argument');
      }
      args.url = `${getApiRoot()}${args.url.substring(0, 1) === '/' ? '' : '/'}${args.url}`;
  
      if (args.girderToken) {
          args.headers = args.headers || {};
          args.headers['Girder-Token'] = args.girderToken;
          delete args.girderToken;
      }
  
      return Backbone.$.ajax(args);
  }
func_documentation_string: Make a request to the REST API.
  
  This is a wrapper around {@link http://api.jquery.com/jQuery.ajax/ $.ajax}, which also handles
  authentication and routing to the Girder API, and provides a default for error notification.
  
  Most users of this method should attach a "done" handler to the return value. In cases where the
  server is ordinarily expected to return a non-200 status (e.g. validating user input), the
  "error: null" argument should probably be provided, and errors should be processed via an
  attached "fail" handler.
  
  Before this function is called, the API root must be set (which typically happens automatically).
  
  @param {Object} opts Options for the request, most of which will be passed through to $.ajax.
  @param {string} opts.url The resource path, relative to the API root, without leading or trailing
  slashes. e.g. "user/login"
  @param {string} [opts.method='GET'] The HTTP method to invoke.
  @param {string|Object} [opts.data] The query string or form parameter object.
  @param {?Function} [opts.error] An error callback, as documented in
  {@link http://api.jquery.com/jQuery.ajax/ $.ajax}, or null. If not provided, this will
  have a default behavior of triggering a 'g:alert' global event, with details of the
  error, and logging the error to the console. It is recommended that you do not ever pass
  a non-null callback function, and handle errors via promise rejection handlers instead.
  @param {string} [opts.girderToken] An alternative auth token to use for this request.
  @returns {$.Promise} A jqXHR promise, which resolves and rejects
  {@link http://api.jquery.com/jQuery.ajax/#jqXHR as documented by $.ajax}.