We would like to to understand impact on performance data, once gerrit is opened in background tab and on different repos. Change-Id: Id1b44e37a33c849a42193f1c110a789c28c769f3
		
			
				
	
	
		
			1526 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1526 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						|
 * @license
 | 
						|
 * Copyright (C) 2016 The Android Open Source Project
 | 
						|
 *
 | 
						|
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
 * you may not use this file except in compliance with the License.
 | 
						|
 * You may obtain a copy of the License at
 | 
						|
 *
 | 
						|
 * http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 *
 | 
						|
 * Unless required by applicable law or agreed to in writing, software
 | 
						|
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
 * See the License for the specific language governing permissions and
 | 
						|
 * limitations under the License.
 | 
						|
 */
 | 
						|
(function() {
 | 
						|
  'use strict';
 | 
						|
 | 
						|
  const RoutePattern = {
 | 
						|
    ROOT: '/',
 | 
						|
 | 
						|
    DASHBOARD: /^\/dashboard\/(.+)$/,
 | 
						|
    CUSTOM_DASHBOARD: /^\/dashboard\/?$/,
 | 
						|
    PROJECT_DASHBOARD: /^\/p\/(.+)\/\+\/dashboard\/(.+)/,
 | 
						|
 | 
						|
    AGREEMENTS: /^\/settings\/agreements\/?/,
 | 
						|
    NEW_AGREEMENTS: /^\/settings\/new-agreement\/?/,
 | 
						|
    REGISTER: /^\/register(\/.*)?$/,
 | 
						|
 | 
						|
    // Pattern for login and logout URLs intended to be passed-through. May
 | 
						|
    // include a return URL.
 | 
						|
    LOG_IN_OR_OUT: /\/log(in|out)(\/(.+))?$/,
 | 
						|
 | 
						|
    // Pattern for a catchall route when no other pattern is matched.
 | 
						|
    DEFAULT: /.*/,
 | 
						|
 | 
						|
    // Matches /admin/groups/[uuid-]<group>
 | 
						|
    GROUP: /^\/admin\/groups\/(?:uuid-)?([^,]+)$/,
 | 
						|
 | 
						|
    // Redirects /groups/self to /settings/#Groups for GWT compatibility
 | 
						|
    GROUP_SELF: /^\/groups\/self/,
 | 
						|
 | 
						|
    // Matches /admin/groups/[uuid-]<group>,info (backwords compat with gwtui)
 | 
						|
    // Redirects to /admin/groups/[uuid-]<group>
 | 
						|
    GROUP_INFO: /^\/admin\/groups\/(?:uuid-)?(.+),info$/,
 | 
						|
 | 
						|
    // Matches /admin/groups/<group>,audit-log
 | 
						|
    GROUP_AUDIT_LOG: /^\/admin\/groups\/(?:uuid-)?(.+),audit-log$/,
 | 
						|
 | 
						|
    // Matches /admin/groups/[uuid-]<group>,members
 | 
						|
    GROUP_MEMBERS: /^\/admin\/groups\/(?:uuid-)?(.+),members$/,
 | 
						|
 | 
						|
    // Matches /admin/groups[,<offset>][/].
 | 
						|
    GROUP_LIST_OFFSET: /^\/admin\/groups(,(\d+))?(\/)?$/,
 | 
						|
    GROUP_LIST_FILTER: '/admin/groups/q/filter::filter',
 | 
						|
    GROUP_LIST_FILTER_OFFSET: '/admin/groups/q/filter::filter,:offset',
 | 
						|
 | 
						|
    // Matches /admin/create-project
 | 
						|
    LEGACY_CREATE_PROJECT: /^\/admin\/create-project\/?$/,
 | 
						|
 | 
						|
    // Matches /admin/create-project
 | 
						|
    LEGACY_CREATE_GROUP: /^\/admin\/create-group\/?$/,
 | 
						|
 | 
						|
    PROJECT_OLD: /^\/admin\/(projects)\/?(.+)?$/,
 | 
						|
 | 
						|
    // Matches /admin/repos/<repo>
 | 
						|
    REPO: /^\/admin\/repos\/([^,]+)$/,
 | 
						|
 | 
						|
    // Matches /admin/repos/<repo>,commands.
 | 
						|
    REPO_COMMANDS: /^\/admin\/repos\/(.+),commands$/,
 | 
						|
 | 
						|
    // Matches /admin/repos/<repos>,access.
 | 
						|
    REPO_ACCESS: /^\/admin\/repos\/(.+),access$/,
 | 
						|
 | 
						|
    // Matches /admin/repos/<repos>,access.
 | 
						|
    REPO_DASHBOARDS: /^\/admin\/repos\/(.+),dashboards$/,
 | 
						|
 | 
						|
    // Matches /admin/repos[,<offset>][/].
 | 
						|
    REPO_LIST_OFFSET: /^\/admin\/repos(,(\d+))?(\/)?$/,
 | 
						|
    REPO_LIST_FILTER: '/admin/repos/q/filter::filter',
 | 
						|
    REPO_LIST_FILTER_OFFSET: '/admin/repos/q/filter::filter,:offset',
 | 
						|
 | 
						|
    // Matches /admin/repos/<repo>,branches[,<offset>].
 | 
						|
    BRANCH_LIST_OFFSET: /^\/admin\/repos\/(.+),branches(,(.+))?$/,
 | 
						|
    BRANCH_LIST_FILTER: '/admin/repos/:repo,branches/q/filter::filter',
 | 
						|
    BRANCH_LIST_FILTER_OFFSET:
 | 
						|
        '/admin/repos/:repo,branches/q/filter::filter,:offset',
 | 
						|
 | 
						|
    // Matches /admin/repos/<repo>,tags[,<offset>].
 | 
						|
    TAG_LIST_OFFSET: /^\/admin\/repos\/(.+),tags(,(.+))?$/,
 | 
						|
    TAG_LIST_FILTER: '/admin/repos/:repo,tags/q/filter::filter',
 | 
						|
    TAG_LIST_FILTER_OFFSET:
 | 
						|
        '/admin/repos/:repo,tags/q/filter::filter,:offset',
 | 
						|
 | 
						|
    PLUGINS: /^\/plugins\/(.+)$/,
 | 
						|
 | 
						|
    PLUGIN_LIST: /^\/admin\/plugins(\/)?$/,
 | 
						|
 | 
						|
    // Matches /admin/plugins[,<offset>][/].
 | 
						|
    PLUGIN_LIST_OFFSET: /^\/admin\/plugins(,(\d+))?(\/)?$/,
 | 
						|
    PLUGIN_LIST_FILTER: '/admin/plugins/q/filter::filter',
 | 
						|
    PLUGIN_LIST_FILTER_OFFSET: '/admin/plugins/q/filter::filter,:offset',
 | 
						|
 | 
						|
    QUERY: /^\/q\/([^,]+)(,(\d+))?$/,
 | 
						|
 | 
						|
    /**
 | 
						|
     * Support vestigial params from GWT UI.
 | 
						|
     * @see Issue 7673.
 | 
						|
     * @type {!RegExp}
 | 
						|
     */
 | 
						|
    QUERY_LEGACY_SUFFIX: /^\/q\/.+,n,z$/,
 | 
						|
 | 
						|
    // Matches /c/<changeNum>/[<basePatchNum>..][<patchNum>][/].
 | 
						|
    CHANGE_LEGACY: /^\/c\/(\d+)\/?(((-?\d+|edit)(\.\.(\d+|edit))?))?\/?$/,
 | 
						|
    CHANGE_NUMBER_LEGACY: /^\/(\d+)\/?/,
 | 
						|
 | 
						|
    // Matches
 | 
						|
    // /c/<project>/+/<changeNum>/[<basePatchNum|edit>..][<patchNum|edit>].
 | 
						|
    // TODO(kaspern): Migrate completely to project based URLs, with backwards
 | 
						|
    // compatibility for change-only.
 | 
						|
    CHANGE: /^\/c\/(.+)\/\+\/(\d+)(\/?((-?\d+|edit)(\.\.(\d+|edit))?))?\/?$/,
 | 
						|
 | 
						|
    // Matches /c/<project>/+/<changeNum>/[<patchNum|edit>],edit
 | 
						|
    CHANGE_EDIT: /^\/c\/(.+)\/\+\/(\d+)(\/(\d+))?,edit\/?$/,
 | 
						|
 | 
						|
    // Matches
 | 
						|
    // /c/<project>/+/<changeNum>/[<basePatchNum|edit>..]<patchNum|edit>/<path>.
 | 
						|
    // TODO(kaspern): Migrate completely to project based URLs, with backwards
 | 
						|
    // compatibility for change-only.
 | 
						|
    // eslint-disable-next-line max-len
 | 
						|
    DIFF: /^\/c\/(.+)\/\+\/(\d+)(\/((-?\d+|edit)(\.\.(\d+|edit))?(\/(.+))))\/?$/,
 | 
						|
 | 
						|
    // Matches /c/<project>/+/<changeNum>/[<patchNum|edit>]/<path>,edit
 | 
						|
    DIFF_EDIT: /^\/c\/(.+)\/\+\/(\d+)\/(\d+|edit)\/(.+),edit$/,
 | 
						|
 | 
						|
    // Matches non-project-relative
 | 
						|
    // /c/<changeNum>/[<basePatchNum>..]<patchNum>/<path>.
 | 
						|
    DIFF_LEGACY: /^\/c\/(\d+)\/((-?\d+|edit)(\.\.(\d+|edit))?)\/(.+)/,
 | 
						|
 | 
						|
    // Matches diff routes using @\d+ to specify a file name (whether or not
 | 
						|
    // the project name is included).
 | 
						|
    // eslint-disable-next-line max-len
 | 
						|
    DIFF_LEGACY_LINENUM: /^\/c\/((.+)\/\+\/)?(\d+)(\/?((-?\d+|edit)(\.\.(\d+|edit))?\/(.+))?)@[ab]?\d+$/,
 | 
						|
 | 
						|
    SETTINGS: /^\/settings\/?/,
 | 
						|
    SETTINGS_LEGACY: /^\/settings\/VE\/(\S+)/,
 | 
						|
 | 
						|
    // Matches /c/<changeNum>/ /<URL tail>
 | 
						|
    // Catches improperly encoded URLs (context: Issue 7100)
 | 
						|
    IMPROPERLY_ENCODED_PLUS: /^\/c\/(.+)\/\ \/(.+)$/,
 | 
						|
 | 
						|
    PLUGIN_SCREEN: /^\/x\/([\w-]+)\/([\w-]+)\/?/,
 | 
						|
 | 
						|
    DOCUMENTATION_SEARCH_FILTER: '/Documentation/q/filter::filter',
 | 
						|
    DOCUMENTATION_SEARCH: /^\/Documentation\/q\/(.*)$/,
 | 
						|
    DOCUMENTATION: /^\/Documentation(\/)?(.+)?/,
 | 
						|
  };
 | 
						|
 | 
						|
  /**
 | 
						|
   * Pattern to recognize and parse the diff line locations as they appear in
 | 
						|
   * the hash of diff URLs. In this format, a number on its own indicates that
 | 
						|
   * line number in the revision of the diff. A number prefixed by either an 'a'
 | 
						|
   * or a 'b' indicates that line number of the base of the diff.
 | 
						|
   * @type {RegExp}
 | 
						|
   */
 | 
						|
  const LINE_ADDRESS_PATTERN = /^([ab]?)(\d+)$/;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Pattern to recognize '+' in url-encoded strings for replacement with ' '.
 | 
						|
   */
 | 
						|
  const PLUS_PATTERN = /\+/g;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Pattern to recognize leading '?' in window.location.search, for stripping.
 | 
						|
   */
 | 
						|
  const QUESTION_PATTERN = /^\?*/;
 | 
						|
 | 
						|
  /**
 | 
						|
   * GWT UI would use @\d+ at the end of a path to indicate linenum.
 | 
						|
   */
 | 
						|
  const LEGACY_LINENUM_PATTERN = /@([ab]?\d+)$/;
 | 
						|
 | 
						|
  const LEGACY_QUERY_SUFFIX_PATTERN = /,n,z$/;
 | 
						|
 | 
						|
  const REPO_TOKEN_PATTERN = /\$\{(project|repo)\}/g;
 | 
						|
 | 
						|
  // Polymer makes `app` intrinsically defined on the window by virtue of the
 | 
						|
  // custom element having the id "app", but it is made explicit here.
 | 
						|
  const app = document.querySelector('#app');
 | 
						|
  if (!app) {
 | 
						|
    console.log('No gr-app found (running tests)');
 | 
						|
  }
 | 
						|
 | 
						|
  // Setup listeners outside of the router component initialization.
 | 
						|
  (function() {
 | 
						|
    const reporting = document.createElement('gr-reporting');
 | 
						|
 | 
						|
    window.addEventListener('load', () => {
 | 
						|
      setTimeout(() => {
 | 
						|
        reporting.pageLoaded();
 | 
						|
      }, 0);
 | 
						|
    });
 | 
						|
 | 
						|
    window.addEventListener('WebComponentsReady', () => {
 | 
						|
      reporting.timeEnd('WebComponentsReady');
 | 
						|
    });
 | 
						|
  })();
 | 
						|
 | 
						|
  Polymer({
 | 
						|
    is: 'gr-router',
 | 
						|
    _legacyUndefinedCheck: true,
 | 
						|
 | 
						|
    properties: {
 | 
						|
      _app: {
 | 
						|
        type: Object,
 | 
						|
        value: app,
 | 
						|
      },
 | 
						|
      _isRedirecting: Boolean,
 | 
						|
      // This variable is to differentiate between internal navigation (false)
 | 
						|
      // and for first navigation in app after loaded from server (true).
 | 
						|
      _isInitialLoad: {
 | 
						|
        type: Boolean,
 | 
						|
        value: true,
 | 
						|
      },
 | 
						|
    },
 | 
						|
 | 
						|
    behaviors: [
 | 
						|
      Gerrit.BaseUrlBehavior,
 | 
						|
      Gerrit.FireBehavior,
 | 
						|
      Gerrit.PatchSetBehavior,
 | 
						|
      Gerrit.URLEncodingBehavior,
 | 
						|
    ],
 | 
						|
 | 
						|
    start() {
 | 
						|
      if (!this._app) { return; }
 | 
						|
      this._startRouter();
 | 
						|
    },
 | 
						|
 | 
						|
    _setParams(params) {
 | 
						|
      this._appElement().params = params;
 | 
						|
    },
 | 
						|
 | 
						|
    _appElement() {
 | 
						|
      // In Polymer2 you have to reach through the shadow root of the app
 | 
						|
      // element. This obviously breaks encapsulation.
 | 
						|
      // TODO(brohlfs): Make this more elegant, e.g. by exposing app-element
 | 
						|
      // explicitly in app, or by delegating to it.
 | 
						|
      return document.getElementById('app-element') ||
 | 
						|
          document.getElementById('app').shadowRoot.getElementById(
 | 
						|
              'app-element');
 | 
						|
    },
 | 
						|
 | 
						|
    _redirect(url) {
 | 
						|
      this._isRedirecting = true;
 | 
						|
      page.redirect(url);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param {!Object} params
 | 
						|
     * @return {string}
 | 
						|
     */
 | 
						|
    _generateUrl(params) {
 | 
						|
      const base = this.getBaseUrl();
 | 
						|
      let url = '';
 | 
						|
      const Views = Gerrit.Nav.View;
 | 
						|
 | 
						|
      if (params.view === Views.SEARCH) {
 | 
						|
        url = this._generateSearchUrl(params);
 | 
						|
      } else if (params.view === Views.CHANGE) {
 | 
						|
        url = this._generateChangeUrl(params);
 | 
						|
      } else if (params.view === Views.DASHBOARD) {
 | 
						|
        url = this._generateDashboardUrl(params);
 | 
						|
      } else if (params.view === Views.DIFF || params.view === Views.EDIT) {
 | 
						|
        url = this._generateDiffOrEditUrl(params);
 | 
						|
      } else if (params.view === Views.GROUP) {
 | 
						|
        url = this._generateGroupUrl(params);
 | 
						|
      } else if (params.view === Views.REPO) {
 | 
						|
        url = this._generateRepoUrl(params);
 | 
						|
      } else if (params.view === Views.ROOT) {
 | 
						|
        url = '/';
 | 
						|
      } else if (params.view === Views.SETTINGS) {
 | 
						|
        url = this._generateSettingsUrl(params);
 | 
						|
      } else {
 | 
						|
        throw new Error('Can\'t generate');
 | 
						|
      }
 | 
						|
 | 
						|
      return base + url;
 | 
						|
    },
 | 
						|
 | 
						|
    _generateWeblinks(params) {
 | 
						|
      const type = params.type;
 | 
						|
      switch (type) {
 | 
						|
        case Gerrit.Nav.WeblinkType.FILE:
 | 
						|
          return this._getFileWebLinks(params);
 | 
						|
        case Gerrit.Nav.WeblinkType.CHANGE:
 | 
						|
          return this._getChangeWeblinks(params);
 | 
						|
        case Gerrit.Nav.WeblinkType.PATCHSET:
 | 
						|
          return this._getPatchSetWeblink(params);
 | 
						|
        default:
 | 
						|
          console.warn(`Unsupported weblink ${type}!`);
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    _getPatchSetWeblink(params) {
 | 
						|
      const {commit, options} = params;
 | 
						|
      const {weblinks, config} = options || {};
 | 
						|
      const name = commit && commit.slice(0, 7);
 | 
						|
      const weblink = this._getBrowseCommitWeblink(weblinks, config);
 | 
						|
      if (!weblink || !weblink.url) {
 | 
						|
        return {name};
 | 
						|
      } else {
 | 
						|
        return {name, url: weblink.url};
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    _firstCodeBrowserWeblink(weblinks) {
 | 
						|
      // This is an ordered whitelist of web link types that provide direct
 | 
						|
      // links to the commit in the url property.
 | 
						|
      const codeBrowserLinks = ['gitiles', 'browse', 'gitweb'];
 | 
						|
      for (let i = 0; i < codeBrowserLinks.length; i++) {
 | 
						|
        const weblink =
 | 
						|
          weblinks.find(weblink => weblink.name === codeBrowserLinks[i]);
 | 
						|
        if (weblink) { return weblink; }
 | 
						|
      }
 | 
						|
      return null;
 | 
						|
    },
 | 
						|
 | 
						|
 | 
						|
    _getBrowseCommitWeblink(weblinks, config) {
 | 
						|
      if (!weblinks) { return null; }
 | 
						|
      let weblink;
 | 
						|
      // Use primary weblink if configured and exists.
 | 
						|
      if (config && config.gerrit && config.gerrit.primary_weblink_name) {
 | 
						|
        weblink = weblinks.find(
 | 
						|
            weblink => weblink.name === config.gerrit.primary_weblink_name
 | 
						|
        );
 | 
						|
      }
 | 
						|
      if (!weblink) {
 | 
						|
        weblink = this._firstCodeBrowserWeblink(weblinks);
 | 
						|
      }
 | 
						|
      if (!weblink) { return null; }
 | 
						|
      return weblink;
 | 
						|
    },
 | 
						|
 | 
						|
    _getChangeWeblinks({repo, commit, options: {weblinks, config}}) {
 | 
						|
      if (!weblinks || !weblinks.length) return [];
 | 
						|
      const commitWeblink = this._getBrowseCommitWeblink(weblinks, config);
 | 
						|
      return weblinks.filter(weblink =>
 | 
						|
        !commitWeblink ||
 | 
						|
        !commitWeblink.name ||
 | 
						|
        weblink.name !== commitWeblink.name);
 | 
						|
    },
 | 
						|
 | 
						|
    _getFileWebLinks({repo, commit, file, options: {weblinks}}) {
 | 
						|
      return weblinks;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param {!Object} params
 | 
						|
     * @return {string}
 | 
						|
     */
 | 
						|
    _generateSearchUrl(params) {
 | 
						|
      let offsetExpr = '';
 | 
						|
      if (params.offset && params.offset > 0) {
 | 
						|
        offsetExpr = ',' + params.offset;
 | 
						|
      }
 | 
						|
 | 
						|
      if (params.query) {
 | 
						|
        return '/q/' + this.encodeURL(params.query, true) + offsetExpr;
 | 
						|
      }
 | 
						|
 | 
						|
      const operators = [];
 | 
						|
      if (params.owner) {
 | 
						|
        operators.push('owner:' + this.encodeURL(params.owner, false));
 | 
						|
      }
 | 
						|
      if (params.project) {
 | 
						|
        operators.push('project:' + this.encodeURL(params.project, false));
 | 
						|
      }
 | 
						|
      if (params.branch) {
 | 
						|
        operators.push('branch:' + this.encodeURL(params.branch, false));
 | 
						|
      }
 | 
						|
      if (params.topic) {
 | 
						|
        operators.push('topic:"' + this.encodeURL(params.topic, false) + '"');
 | 
						|
      }
 | 
						|
      if (params.hashtag) {
 | 
						|
        operators.push('hashtag:"' +
 | 
						|
            this.encodeURL(params.hashtag.toLowerCase(), false) + '"');
 | 
						|
      }
 | 
						|
      if (params.statuses) {
 | 
						|
        if (params.statuses.length === 1) {
 | 
						|
          operators.push(
 | 
						|
              'status:' + this.encodeURL(params.statuses[0], false));
 | 
						|
        } else if (params.statuses.length > 1) {
 | 
						|
          operators.push(
 | 
						|
              '(' +
 | 
						|
              params.statuses.map(s => `status:${this.encodeURL(s, false)}`)
 | 
						|
                  .join(' OR ') +
 | 
						|
              ')');
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      return '/q/' + operators.join('+') + offsetExpr;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param {!Object} params
 | 
						|
     * @return {string}
 | 
						|
     */
 | 
						|
    _generateChangeUrl(params) {
 | 
						|
      let range = this._getPatchRangeExpression(params);
 | 
						|
      if (range.length) { range = '/' + range; }
 | 
						|
      let suffix = `${range}`;
 | 
						|
      if (params.querystring) {
 | 
						|
        suffix += '?' + params.querystring;
 | 
						|
      } else if (params.edit) {
 | 
						|
        suffix += ',edit';
 | 
						|
      }
 | 
						|
      if (params.messageHash) {
 | 
						|
        suffix += params.messageHash;
 | 
						|
      }
 | 
						|
      if (params.project) {
 | 
						|
        const encodedProject = this.encodeURL(params.project, true);
 | 
						|
        return `/c/${encodedProject}/+/${params.changeNum}${suffix}`;
 | 
						|
      } else {
 | 
						|
        return `/c/${params.changeNum}${suffix}`;
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param {!Object} params
 | 
						|
     * @return {string}
 | 
						|
     */
 | 
						|
    _generateDashboardUrl(params) {
 | 
						|
      const repoName = params.repo || params.project || null;
 | 
						|
      if (params.sections) {
 | 
						|
        // Custom dashboard.
 | 
						|
        const queryParams = this._sectionsToEncodedParams(params.sections,
 | 
						|
            repoName);
 | 
						|
        if (params.title) {
 | 
						|
          queryParams.push('title=' + encodeURIComponent(params.title));
 | 
						|
        }
 | 
						|
        const user = params.user ? params.user : '';
 | 
						|
        return `/dashboard/${user}?${queryParams.join('&')}`;
 | 
						|
      } else if (repoName) {
 | 
						|
        // Project dashboard.
 | 
						|
        const encodedRepo = this.encodeURL(repoName, true);
 | 
						|
        return `/p/${encodedRepo}/+/dashboard/${params.dashboard}`;
 | 
						|
      } else {
 | 
						|
        // User dashboard.
 | 
						|
        return `/dashboard/${params.user || 'self'}`;
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param {!Array<!{name: string, query: string}>} sections
 | 
						|
     * @param {string=} opt_repoName
 | 
						|
     * @return {!Array<string>}
 | 
						|
     */
 | 
						|
    _sectionsToEncodedParams(sections, opt_repoName) {
 | 
						|
      return sections.map(section => {
 | 
						|
        // If there is a repo name provided, make sure to substitute it into the
 | 
						|
        // ${repo} (or legacy ${project}) query tokens.
 | 
						|
        const query = opt_repoName ?
 | 
						|
            section.query.replace(REPO_TOKEN_PATTERN, opt_repoName) :
 | 
						|
            section.query;
 | 
						|
        return encodeURIComponent(section.name) + '=' +
 | 
						|
            encodeURIComponent(query);
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param {!Object} params
 | 
						|
     * @return {string}
 | 
						|
     */
 | 
						|
    _generateDiffOrEditUrl(params) {
 | 
						|
      let range = this._getPatchRangeExpression(params);
 | 
						|
      if (range.length) { range = '/' + range; }
 | 
						|
 | 
						|
      let suffix = `${range}/${this.encodeURL(params.path, true)}`;
 | 
						|
 | 
						|
      if (params.view === Gerrit.Nav.View.EDIT) { suffix += ',edit'; }
 | 
						|
 | 
						|
      if (params.lineNum) {
 | 
						|
        suffix += '#';
 | 
						|
        if (params.leftSide) { suffix += 'b'; }
 | 
						|
        suffix += params.lineNum;
 | 
						|
      }
 | 
						|
 | 
						|
      if (params.project) {
 | 
						|
        const encodedProject = this.encodeURL(params.project, true);
 | 
						|
        return `/c/${encodedProject}/+/${params.changeNum}${suffix}`;
 | 
						|
      } else {
 | 
						|
        return `/c/${params.changeNum}${suffix}`;
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param {!Object} params
 | 
						|
     * @return {string}
 | 
						|
     */
 | 
						|
    _generateGroupUrl(params) {
 | 
						|
      let url = `/admin/groups/${this.encodeURL(params.groupId + '', true)}`;
 | 
						|
      if (params.detail === Gerrit.Nav.GroupDetailView.MEMBERS) {
 | 
						|
        url += ',members';
 | 
						|
      } else if (params.detail === Gerrit.Nav.GroupDetailView.LOG) {
 | 
						|
        url += ',audit-log';
 | 
						|
      }
 | 
						|
      return url;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param {!Object} params
 | 
						|
     * @return {string}
 | 
						|
     */
 | 
						|
    _generateRepoUrl(params) {
 | 
						|
      let url = `/admin/repos/${this.encodeURL(params.repoName + '', true)}`;
 | 
						|
      if (params.detail === Gerrit.Nav.RepoDetailView.ACCESS) {
 | 
						|
        url += ',access';
 | 
						|
      } else if (params.detail === Gerrit.Nav.RepoDetailView.BRANCHES) {
 | 
						|
        url += ',branches';
 | 
						|
      } else if (params.detail === Gerrit.Nav.RepoDetailView.TAGS) {
 | 
						|
        url += ',tags';
 | 
						|
      } else if (params.detail === Gerrit.Nav.RepoDetailView.COMMANDS) {
 | 
						|
        url += ',commands';
 | 
						|
      } else if (params.detail === Gerrit.Nav.RepoDetailView.DASHBOARDS) {
 | 
						|
        url += ',dashboards';
 | 
						|
      }
 | 
						|
      return url;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param {!Object} params
 | 
						|
     * @return {string}
 | 
						|
     */
 | 
						|
    _generateSettingsUrl(params) {
 | 
						|
      return '/settings';
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Given an object of parameters, potentially including a `patchNum` or a
 | 
						|
     * `basePatchNum` or both, return a string representation of that range. If
 | 
						|
     * no range is indicated in the params, the empty string is returned.
 | 
						|
     * @param {!Object} params
 | 
						|
     * @return {string}
 | 
						|
     */
 | 
						|
    _getPatchRangeExpression(params) {
 | 
						|
      let range = '';
 | 
						|
      if (params.patchNum) { range = '' + params.patchNum; }
 | 
						|
      if (params.basePatchNum) { range = params.basePatchNum + '..' + range; }
 | 
						|
      return range;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Given a set of params without a project, gets the project from the rest
 | 
						|
     * API project lookup and then sets the app params.
 | 
						|
     *
 | 
						|
     * @param {?Object} params
 | 
						|
     */
 | 
						|
    _normalizeLegacyRouteParams(params) {
 | 
						|
      if (!params.changeNum) { return Promise.resolve(); }
 | 
						|
 | 
						|
      return this.$.restAPI.getFromProjectLookup(params.changeNum)
 | 
						|
          .then(project => {
 | 
						|
            // Show a 404 and terminate if the lookup request failed. Attempting
 | 
						|
            // to redirect after failing to get the project loops infinitely.
 | 
						|
            if (!project) {
 | 
						|
              this._show404();
 | 
						|
              return;
 | 
						|
            }
 | 
						|
 | 
						|
            params.project = project;
 | 
						|
            this._normalizePatchRangeParams(params);
 | 
						|
            this._redirect(this._generateUrl(params));
 | 
						|
          });
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Normalizes the params object, and determines if the URL needs to be
 | 
						|
     * modified to fit the proper schema.
 | 
						|
     *
 | 
						|
     * @param {*} params
 | 
						|
     * @return {boolean} whether or not the URL needs to be upgraded.
 | 
						|
     */
 | 
						|
    _normalizePatchRangeParams(params) {
 | 
						|
      const hasBasePatchNum = params.basePatchNum !== null &&
 | 
						|
          params.basePatchNum !== undefined;
 | 
						|
      const hasPatchNum = params.patchNum !== null &&
 | 
						|
          params.patchNum !== undefined;
 | 
						|
      let needsRedirect = false;
 | 
						|
 | 
						|
      // Diffing a patch against itself is invalid, so if the base and revision
 | 
						|
      // patches are equal clear the base.
 | 
						|
      if (hasBasePatchNum &&
 | 
						|
          this.patchNumEquals(params.basePatchNum, params.patchNum)) {
 | 
						|
        needsRedirect = true;
 | 
						|
        params.basePatchNum = null;
 | 
						|
      } else if (hasBasePatchNum && !hasPatchNum) {
 | 
						|
        // Regexes set basePatchNum instead of patchNum when only one is
 | 
						|
        // specified. Redirect is not needed in this case.
 | 
						|
        params.patchNum = params.basePatchNum;
 | 
						|
        params.basePatchNum = null;
 | 
						|
      }
 | 
						|
      return needsRedirect;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Redirect the user to login using the given return-URL for redirection
 | 
						|
     * after authentication success.
 | 
						|
     * @param {string} returnUrl
 | 
						|
     */
 | 
						|
    _redirectToLogin(returnUrl) {
 | 
						|
      const basePath = this.getBaseUrl() || '';
 | 
						|
      page(
 | 
						|
          '/login/' + encodeURIComponent(returnUrl.substring(basePath.length)));
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Hashes parsed by page.js exclude "inner" hashes, so a URL like "/a#b#c"
 | 
						|
     * is parsed to have a hash of "b" rather than "b#c". Instead, this method
 | 
						|
     * parses hashes correctly. Will return an empty string if there is no hash.
 | 
						|
     * @param {!string} canonicalPath
 | 
						|
     * @return {!string} Everything after the first '#' ("a#b#c" -> "b#c").
 | 
						|
     */
 | 
						|
    _getHashFromCanonicalPath(canonicalPath) {
 | 
						|
      return canonicalPath.split('#').slice(1).join('#');
 | 
						|
    },
 | 
						|
 | 
						|
    _parseLineAddress(hash) {
 | 
						|
      const match = hash.match(LINE_ADDRESS_PATTERN);
 | 
						|
      if (!match) { return null; }
 | 
						|
      return {
 | 
						|
        leftSide: !!match[1],
 | 
						|
        lineNum: parseInt(match[2], 10),
 | 
						|
      };
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Check to see if the user is logged in and return a promise that only
 | 
						|
     * resolves if the user is logged in. If the user us not logged in, the
 | 
						|
     * promise is rejected and the page is redirected to the login flow.
 | 
						|
     * @param {!Object} data The parsed route data.
 | 
						|
     * @return {!Promise<!Object>} A promise yielding the original route data
 | 
						|
     *     (if it resolves).
 | 
						|
     */
 | 
						|
    _redirectIfNotLoggedIn(data) {
 | 
						|
      return this.$.restAPI.getLoggedIn().then(loggedIn => {
 | 
						|
        if (loggedIn) {
 | 
						|
          return Promise.resolve();
 | 
						|
        } else {
 | 
						|
          this._redirectToLogin(data.canonicalPath);
 | 
						|
          return Promise.reject(new Error());
 | 
						|
        }
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    /**  Page.js middleware that warms the REST API's logged-in cache line. */
 | 
						|
    _loadUserMiddleware(ctx, next) {
 | 
						|
      this.$.restAPI.getLoggedIn().then(() => { next(); });
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Map a route to a method on the router.
 | 
						|
     *
 | 
						|
     * @param {!string|!RegExp} pattern The page.js pattern for the route.
 | 
						|
     * @param {!string} handlerName The method name for the handler. If the
 | 
						|
     *     route is matched, the handler will be executed with `this` referring
 | 
						|
     *     to the component. Its return value will be discarded so that it does
 | 
						|
     *     not interfere with page.js.
 | 
						|
     * @param  {?boolean=} opt_authRedirect If true, then auth is checked before
 | 
						|
     *     executing the handler. If the user is not logged in, it will redirect
 | 
						|
     *     to the login flow and the handler will not be executed. The login
 | 
						|
     *     redirect specifies the matched URL to be used after successfull auth.
 | 
						|
     */
 | 
						|
    _mapRoute(pattern, handlerName, opt_authRedirect) {
 | 
						|
      if (!this[handlerName]) {
 | 
						|
        console.error('Attempted to map route to unknown method: ',
 | 
						|
            handlerName);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      page(pattern, this._loadUserMiddleware.bind(this), data => {
 | 
						|
        this.$.reporting.locationChanged(handlerName);
 | 
						|
        const promise = opt_authRedirect ?
 | 
						|
          this._redirectIfNotLoggedIn(data) : Promise.resolve();
 | 
						|
        promise.then(() => { this[handlerName](data); });
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _startRouter() {
 | 
						|
      const base = this.getBaseUrl();
 | 
						|
      if (base) {
 | 
						|
        page.base(base);
 | 
						|
      }
 | 
						|
 | 
						|
      Gerrit.Nav.setup(
 | 
						|
          url => { page.show(url); },
 | 
						|
          this._generateUrl.bind(this),
 | 
						|
          params => this._generateWeblinks(params),
 | 
						|
          x => x
 | 
						|
      );
 | 
						|
 | 
						|
      page.exit('*', (ctx, next) => {
 | 
						|
        if (!this._isRedirecting) {
 | 
						|
          this.$.reporting.beforeLocationChanged();
 | 
						|
        }
 | 
						|
        this._isRedirecting = false;
 | 
						|
        this._isInitialLoad = false;
 | 
						|
        next();
 | 
						|
      });
 | 
						|
 | 
						|
      // Middleware
 | 
						|
      page((ctx, next) => {
 | 
						|
        document.body.scrollTop = 0;
 | 
						|
 | 
						|
        if (ctx.hash.match(RoutePattern.PLUGIN_SCREEN)) {
 | 
						|
          // Redirect all urls using hash #/x/plugin/screen to /x/plugin/screen
 | 
						|
          // This is needed to allow plugins to add basic #/x/ screen links to
 | 
						|
          // any location.
 | 
						|
          this._redirect(ctx.hash);
 | 
						|
          return;
 | 
						|
        }
 | 
						|
 | 
						|
        // Fire asynchronously so that the URL is changed by the time the event
 | 
						|
        // is processed.
 | 
						|
        this.async(() => {
 | 
						|
          this.fire('location-change', {
 | 
						|
            hash: window.location.hash,
 | 
						|
            pathname: window.location.pathname,
 | 
						|
          });
 | 
						|
        }, 1);
 | 
						|
        next();
 | 
						|
      });
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.ROOT, '_handleRootRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.DASHBOARD, '_handleDashboardRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.CUSTOM_DASHBOARD,
 | 
						|
          '_handleCustomDashboardRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.PROJECT_DASHBOARD,
 | 
						|
          '_handleProjectDashboardRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.GROUP_INFO, '_handleGroupInfoRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.GROUP_AUDIT_LOG, '_handleGroupAuditLogRoute',
 | 
						|
          true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.GROUP_MEMBERS, '_handleGroupMembersRoute',
 | 
						|
          true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.GROUP_LIST_OFFSET,
 | 
						|
          '_handleGroupListOffsetRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.GROUP_LIST_FILTER_OFFSET,
 | 
						|
          '_handleGroupListFilterOffsetRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.GROUP_LIST_FILTER,
 | 
						|
          '_handleGroupListFilterRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.GROUP_SELF, '_handleGroupSelfRedirectRoute',
 | 
						|
          true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.GROUP, '_handleGroupRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.PROJECT_OLD,
 | 
						|
          '_handleProjectsOldRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.REPO_COMMANDS,
 | 
						|
          '_handleRepoCommandsRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.REPO_ACCESS,
 | 
						|
          '_handleRepoAccessRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.REPO_DASHBOARDS,
 | 
						|
          '_handleRepoDashboardsRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.BRANCH_LIST_OFFSET,
 | 
						|
          '_handleBranchListOffsetRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.BRANCH_LIST_FILTER_OFFSET,
 | 
						|
          '_handleBranchListFilterOffsetRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.BRANCH_LIST_FILTER,
 | 
						|
          '_handleBranchListFilterRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.TAG_LIST_OFFSET,
 | 
						|
          '_handleTagListOffsetRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.TAG_LIST_FILTER_OFFSET,
 | 
						|
          '_handleTagListFilterOffsetRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.TAG_LIST_FILTER,
 | 
						|
          '_handleTagListFilterRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.LEGACY_CREATE_GROUP,
 | 
						|
          '_handleCreateGroupRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.LEGACY_CREATE_PROJECT,
 | 
						|
          '_handleCreateProjectRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.REPO_LIST_OFFSET,
 | 
						|
          '_handleRepoListOffsetRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.REPO_LIST_FILTER_OFFSET,
 | 
						|
          '_handleRepoListFilterOffsetRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.REPO_LIST_FILTER,
 | 
						|
          '_handleRepoListFilterRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.REPO, '_handleRepoRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.PLUGINS, '_handlePassThroughRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.PLUGIN_LIST_OFFSET,
 | 
						|
          '_handlePluginListOffsetRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.PLUGIN_LIST_FILTER_OFFSET,
 | 
						|
          '_handlePluginListFilterOffsetRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.PLUGIN_LIST_FILTER,
 | 
						|
          '_handlePluginListFilterRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.PLUGIN_LIST, '_handlePluginListRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.QUERY_LEGACY_SUFFIX,
 | 
						|
          '_handleQueryLegacySuffixRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.QUERY, '_handleQueryRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.DIFF_LEGACY_LINENUM, '_handleLegacyLinenum');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.CHANGE_NUMBER_LEGACY,
 | 
						|
          '_handleChangeNumberLegacyRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.DIFF_EDIT, '_handleDiffEditRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.CHANGE_EDIT, '_handleChangeEditRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.DIFF, '_handleDiffRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.CHANGE, '_handleChangeRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.CHANGE_LEGACY, '_handleChangeLegacyRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.DIFF_LEGACY, '_handleDiffLegacyRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.AGREEMENTS, '_handleAgreementsRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.NEW_AGREEMENTS, '_handleNewAgreementsRoute',
 | 
						|
          true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.SETTINGS_LEGACY,
 | 
						|
          '_handleSettingsLegacyRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.SETTINGS, '_handleSettingsRoute', true);
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.REGISTER, '_handleRegisterRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.LOG_IN_OR_OUT, '_handlePassThroughRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.IMPROPERLY_ENCODED_PLUS,
 | 
						|
          '_handleImproperlyEncodedPlusRoute');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.PLUGIN_SCREEN, '_handlePluginScreen');
 | 
						|
 | 
						|
      this._mapRoute(RoutePattern.DOCUMENTATION_SEARCH_FILTER,
 | 
						|
          '_handleDocumentationSearchRoute');
 | 
						|
 | 
						|
      // redirects /Documentation/q/* to /Documentation/q/filter:*
 | 
						|
      this._mapRoute(RoutePattern.DOCUMENTATION_SEARCH,
 | 
						|
          '_handleDocumentationSearchRedirectRoute');
 | 
						|
 | 
						|
      // Makes sure /Documentation/* links work (doin't return 404)
 | 
						|
      this._mapRoute(RoutePattern.DOCUMENTATION,
 | 
						|
          '_handleDocumentationRedirectRoute');
 | 
						|
 | 
						|
      // Note: this route should appear last so it only catches URLs unmatched
 | 
						|
      // by other patterns.
 | 
						|
      this._mapRoute(RoutePattern.DEFAULT, '_handleDefaultRoute');
 | 
						|
 | 
						|
      page.start();
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param {!Object} data
 | 
						|
     * @return {Promise|null} if handling the route involves asynchrony, then a
 | 
						|
     *     promise is returned. Otherwise, synchronous handling returns null.
 | 
						|
     */
 | 
						|
    _handleRootRoute(data) {
 | 
						|
      if (data.querystring.match(/^closeAfterLogin/)) {
 | 
						|
        // Close child window on redirect after login.
 | 
						|
        window.close();
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
      let hash = this._getHashFromCanonicalPath(data.canonicalPath);
 | 
						|
      // For backward compatibility with GWT links.
 | 
						|
      if (hash) {
 | 
						|
        // In certain login flows the server may redirect to a hash without
 | 
						|
        // a leading slash, which page.js doesn't handle correctly.
 | 
						|
        if (hash[0] !== '/') {
 | 
						|
          hash = '/' + hash;
 | 
						|
        }
 | 
						|
        if (hash.includes('/ /') && data.canonicalPath.includes('/+/')) {
 | 
						|
          // Path decodes all '+' to ' ' -- this breaks project-based URLs.
 | 
						|
          // See Issue 6888.
 | 
						|
          hash = hash.replace('/ /', '/+/');
 | 
						|
        }
 | 
						|
        const base = this.getBaseUrl();
 | 
						|
        let newUrl = base + hash;
 | 
						|
        if (hash.startsWith('/VE/')) {
 | 
						|
          newUrl = base + '/settings' + hash;
 | 
						|
        }
 | 
						|
        this._redirect(newUrl);
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
      return this.$.restAPI.getLoggedIn().then(loggedIn => {
 | 
						|
        if (loggedIn) {
 | 
						|
          this._redirect('/dashboard/self');
 | 
						|
        } else {
 | 
						|
          this._redirect('/q/status:open');
 | 
						|
        }
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Decode an application/x-www-form-urlencoded string.
 | 
						|
     *
 | 
						|
     * @param {string} qs The application/x-www-form-urlencoded string.
 | 
						|
     * @return {string} The decoded string.
 | 
						|
     */
 | 
						|
    _decodeQueryString(qs) {
 | 
						|
      return decodeURIComponent(qs.replace(PLUS_PATTERN, ' '));
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Parse a query string (e.g. window.location.search) into an array of
 | 
						|
     * name/value pairs.
 | 
						|
     *
 | 
						|
     * @param {string} qs The application/x-www-form-urlencoded query string.
 | 
						|
     * @return {!Array<!Array<string>>} An array of name/value pairs, where each
 | 
						|
     *     element is a 2-element array.
 | 
						|
     */
 | 
						|
    _parseQueryString(qs) {
 | 
						|
      qs = qs.replace(QUESTION_PATTERN, '');
 | 
						|
      if (!qs) {
 | 
						|
        return [];
 | 
						|
      }
 | 
						|
      const params = [];
 | 
						|
      qs.split('&').forEach(param => {
 | 
						|
        const idx = param.indexOf('=');
 | 
						|
        let name;
 | 
						|
        let value;
 | 
						|
        if (idx < 0) {
 | 
						|
          name = this._decodeQueryString(param);
 | 
						|
          value = '';
 | 
						|
        } else {
 | 
						|
          name = this._decodeQueryString(param.substring(0, idx));
 | 
						|
          value = this._decodeQueryString(param.substring(idx + 1));
 | 
						|
        }
 | 
						|
        if (name) {
 | 
						|
          params.push([name, value]);
 | 
						|
        }
 | 
						|
      });
 | 
						|
      return params;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Handle dashboard routes. These may be user, or project dashboards.
 | 
						|
     *
 | 
						|
     * @param {!Object} data The parsed route data.
 | 
						|
     */
 | 
						|
    _handleDashboardRoute(data) {
 | 
						|
      // User dashboard. We require viewing user to be logged in, else we
 | 
						|
      // redirect to login for self dashboard or simple owner search for
 | 
						|
      // other user dashboard.
 | 
						|
      return this.$.restAPI.getLoggedIn().then(loggedIn => {
 | 
						|
        if (!loggedIn) {
 | 
						|
          if (data.params[0].toLowerCase() === 'self') {
 | 
						|
            this._redirectToLogin(data.canonicalPath);
 | 
						|
          } else {
 | 
						|
            this._redirect('/q/owner:' + encodeURIComponent(data.params[0]));
 | 
						|
          }
 | 
						|
        } else {
 | 
						|
          this._setParams({
 | 
						|
            view: Gerrit.Nav.View.DASHBOARD,
 | 
						|
            user: data.params[0],
 | 
						|
          });
 | 
						|
        }
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Handle custom dashboard routes.
 | 
						|
     *
 | 
						|
     * @param {!Object} data The parsed route data.
 | 
						|
     * @param {string=} opt_qs Optional query string associated with the route.
 | 
						|
     *     If not given, window.location.search is used. (Used by tests).
 | 
						|
     */
 | 
						|
    _handleCustomDashboardRoute(data, opt_qs) {
 | 
						|
      // opt_qs may be provided by a test, and it may have a falsy value
 | 
						|
      const qs = opt_qs !== undefined ? opt_qs : window.location.search;
 | 
						|
      const queryParams = this._parseQueryString(qs);
 | 
						|
      let title = 'Custom Dashboard';
 | 
						|
      const titleParam = queryParams.find(
 | 
						|
          elem => elem[0].toLowerCase() === 'title');
 | 
						|
      if (titleParam) {
 | 
						|
        title = titleParam[1];
 | 
						|
      }
 | 
						|
      // Dashboards support a foreach param which adds a base query to any
 | 
						|
      // additional query.
 | 
						|
      const forEachParam = queryParams.find(
 | 
						|
          elem => elem[0].toLowerCase() === 'foreach');
 | 
						|
      let forEachQuery = null;
 | 
						|
      if (forEachParam) {
 | 
						|
        forEachQuery = forEachParam[1];
 | 
						|
      }
 | 
						|
      const sectionParams = queryParams.filter(
 | 
						|
          elem => elem[0] && elem[1] && elem[0].toLowerCase() !== 'title'
 | 
						|
          && elem[0].toLowerCase() !== 'foreach');
 | 
						|
      const sections = sectionParams.map(elem => {
 | 
						|
        const query = forEachQuery ? `${forEachQuery} ${elem[1]}` : elem[1];
 | 
						|
        return {
 | 
						|
          name: elem[0],
 | 
						|
          query,
 | 
						|
        };
 | 
						|
      });
 | 
						|
 | 
						|
      if (sections.length > 0) {
 | 
						|
        // Custom dashboard view.
 | 
						|
        this._setParams({
 | 
						|
          view: Gerrit.Nav.View.DASHBOARD,
 | 
						|
          user: 'self',
 | 
						|
          sections,
 | 
						|
          title,
 | 
						|
        });
 | 
						|
        return Promise.resolve();
 | 
						|
      }
 | 
						|
 | 
						|
      // Redirect /dashboard/ -> /dashboard/self.
 | 
						|
      this._redirect('/dashboard/self');
 | 
						|
      return Promise.resolve();
 | 
						|
    },
 | 
						|
 | 
						|
    _handleProjectDashboardRoute(data) {
 | 
						|
      const project = data.params[0];
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.DASHBOARD,
 | 
						|
        project,
 | 
						|
        dashboard: decodeURIComponent(data.params[1]),
 | 
						|
      });
 | 
						|
      this.$.reporting.setRepoName(project);
 | 
						|
    },
 | 
						|
 | 
						|
    _handleGroupInfoRoute(data) {
 | 
						|
      this._redirect('/admin/groups/' + encodeURIComponent(data.params[0]));
 | 
						|
    },
 | 
						|
 | 
						|
    _handleGroupSelfRedirectRoute(data) {
 | 
						|
      this._redirect('/settings/#Groups');
 | 
						|
    },
 | 
						|
 | 
						|
    _handleGroupRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.GROUP,
 | 
						|
        groupId: data.params[0],
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleGroupAuditLogRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.GROUP,
 | 
						|
        detail: Gerrit.Nav.GroupDetailView.LOG,
 | 
						|
        groupId: data.params[0],
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleGroupMembersRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.GROUP,
 | 
						|
        detail: Gerrit.Nav.GroupDetailView.MEMBERS,
 | 
						|
        groupId: data.params[0],
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleGroupListOffsetRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.ADMIN,
 | 
						|
        adminView: 'gr-admin-group-list',
 | 
						|
        offset: data.params[1] || 0,
 | 
						|
        filter: null,
 | 
						|
        openCreateModal: data.hash === 'create',
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleGroupListFilterOffsetRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.ADMIN,
 | 
						|
        adminView: 'gr-admin-group-list',
 | 
						|
        offset: data.params.offset,
 | 
						|
        filter: data.params.filter,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleGroupListFilterRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.ADMIN,
 | 
						|
        adminView: 'gr-admin-group-list',
 | 
						|
        filter: data.params.filter || null,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleProjectsOldRoute(data) {
 | 
						|
      let params = '';
 | 
						|
      if (data.params[1]) {
 | 
						|
        params = encodeURIComponent(data.params[1]);
 | 
						|
        if (data.params[1].includes(',')) {
 | 
						|
          params =
 | 
						|
              encodeURIComponent(data.params[1]).replace('%2C', ',');
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      this._redirect(`/admin/repos/${params}`);
 | 
						|
    },
 | 
						|
 | 
						|
    _handleRepoCommandsRoute(data) {
 | 
						|
      const repo = data.params[0];
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.REPO,
 | 
						|
        detail: Gerrit.Nav.RepoDetailView.COMMANDS,
 | 
						|
        repo,
 | 
						|
      });
 | 
						|
      this.$.reporting.setRepoName(repo);
 | 
						|
    },
 | 
						|
 | 
						|
    _handleRepoAccessRoute(data) {
 | 
						|
      const repo = data.params[0];
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.REPO,
 | 
						|
        detail: Gerrit.Nav.RepoDetailView.ACCESS,
 | 
						|
        repo,
 | 
						|
      });
 | 
						|
      this.$.reporting.setRepoName(repo);
 | 
						|
    },
 | 
						|
 | 
						|
    _handleRepoDashboardsRoute(data) {
 | 
						|
      const repo = data.params[0];
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.REPO,
 | 
						|
        detail: Gerrit.Nav.RepoDetailView.DASHBOARDS,
 | 
						|
        repo,
 | 
						|
      });
 | 
						|
      this.$.reporting.setRepoName(repo);
 | 
						|
    },
 | 
						|
 | 
						|
    _handleBranchListOffsetRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.REPO,
 | 
						|
        detail: Gerrit.Nav.RepoDetailView.BRANCHES,
 | 
						|
        repo: data.params[0],
 | 
						|
        offset: data.params[2] || 0,
 | 
						|
        filter: null,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleBranchListFilterOffsetRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.REPO,
 | 
						|
        detail: Gerrit.Nav.RepoDetailView.BRANCHES,
 | 
						|
        repo: data.params.repo,
 | 
						|
        offset: data.params.offset,
 | 
						|
        filter: data.params.filter,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleBranchListFilterRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.REPO,
 | 
						|
        detail: Gerrit.Nav.RepoDetailView.BRANCHES,
 | 
						|
        repo: data.params.repo,
 | 
						|
        filter: data.params.filter || null,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleTagListOffsetRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.REPO,
 | 
						|
        detail: Gerrit.Nav.RepoDetailView.TAGS,
 | 
						|
        repo: data.params[0],
 | 
						|
        offset: data.params[2] || 0,
 | 
						|
        filter: null,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleTagListFilterOffsetRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.REPO,
 | 
						|
        detail: Gerrit.Nav.RepoDetailView.TAGS,
 | 
						|
        repo: data.params.repo,
 | 
						|
        offset: data.params.offset,
 | 
						|
        filter: data.params.filter,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleTagListFilterRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.REPO,
 | 
						|
        detail: Gerrit.Nav.RepoDetailView.TAGS,
 | 
						|
        repo: data.params.repo,
 | 
						|
        filter: data.params.filter || null,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleRepoListOffsetRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.ADMIN,
 | 
						|
        adminView: 'gr-repo-list',
 | 
						|
        offset: data.params[1] || 0,
 | 
						|
        filter: null,
 | 
						|
        openCreateModal: data.hash === 'create',
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleRepoListFilterOffsetRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.ADMIN,
 | 
						|
        adminView: 'gr-repo-list',
 | 
						|
        offset: data.params.offset,
 | 
						|
        filter: data.params.filter,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleRepoListFilterRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.ADMIN,
 | 
						|
        adminView: 'gr-repo-list',
 | 
						|
        filter: data.params.filter || null,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleCreateProjectRoute(data) {
 | 
						|
      // Redirects the legacy route to the new route, which displays the project
 | 
						|
      // list with a hash 'create'.
 | 
						|
      this._redirect('/admin/repos#create');
 | 
						|
    },
 | 
						|
 | 
						|
    _handleCreateGroupRoute(data) {
 | 
						|
      // Redirects the legacy route to the new route, which displays the group
 | 
						|
      // list with a hash 'create'.
 | 
						|
      this._redirect('/admin/groups#create');
 | 
						|
    },
 | 
						|
 | 
						|
    _handleRepoRoute(data) {
 | 
						|
      const repo = data.params[0];
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.REPO,
 | 
						|
        repo,
 | 
						|
      });
 | 
						|
      this.$.reporting.setRepoName(repo);
 | 
						|
    },
 | 
						|
 | 
						|
    _handlePluginListOffsetRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.ADMIN,
 | 
						|
        adminView: 'gr-plugin-list',
 | 
						|
        offset: data.params[1] || 0,
 | 
						|
        filter: null,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handlePluginListFilterOffsetRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.ADMIN,
 | 
						|
        adminView: 'gr-plugin-list',
 | 
						|
        offset: data.params.offset,
 | 
						|
        filter: data.params.filter,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handlePluginListFilterRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.ADMIN,
 | 
						|
        adminView: 'gr-plugin-list',
 | 
						|
        filter: data.params.filter || null,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handlePluginListRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.ADMIN,
 | 
						|
        adminView: 'gr-plugin-list',
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleQueryRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.SEARCH,
 | 
						|
        query: data.params[0],
 | 
						|
        offset: data.params[2],
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleQueryLegacySuffixRoute(ctx) {
 | 
						|
      this._redirect(ctx.path.replace(LEGACY_QUERY_SUFFIX_PATTERN, ''));
 | 
						|
    },
 | 
						|
 | 
						|
    _handleChangeNumberLegacyRoute(ctx) {
 | 
						|
      this._redirect('/c/' + encodeURIComponent(ctx.params[0]));
 | 
						|
    },
 | 
						|
 | 
						|
    _handleChangeRoute(ctx) {
 | 
						|
      // Parameter order is based on the regex group number matched.
 | 
						|
      const params = {
 | 
						|
        project: ctx.params[0],
 | 
						|
        changeNum: ctx.params[1],
 | 
						|
        basePatchNum: ctx.params[4],
 | 
						|
        patchNum: ctx.params[6],
 | 
						|
        view: Gerrit.Nav.View.CHANGE,
 | 
						|
      };
 | 
						|
 | 
						|
      this.$.reporting.setRepoName(params.project);
 | 
						|
      this._redirectOrNavigate(params);
 | 
						|
    },
 | 
						|
 | 
						|
    _handleDiffRoute(ctx) {
 | 
						|
      // Parameter order is based on the regex group number matched.
 | 
						|
      const params = {
 | 
						|
        project: ctx.params[0],
 | 
						|
        changeNum: ctx.params[1],
 | 
						|
        basePatchNum: ctx.params[4],
 | 
						|
        patchNum: ctx.params[6],
 | 
						|
        path: ctx.params[8],
 | 
						|
        view: Gerrit.Nav.View.DIFF,
 | 
						|
      };
 | 
						|
 | 
						|
      const address = this._parseLineAddress(ctx.hash);
 | 
						|
      if (address) {
 | 
						|
        params.leftSide = address.leftSide;
 | 
						|
        params.lineNum = address.lineNum;
 | 
						|
      }
 | 
						|
      this.$.reporting.setRepoName(params.project);
 | 
						|
      this._redirectOrNavigate(params);
 | 
						|
    },
 | 
						|
 | 
						|
    _handleChangeLegacyRoute(ctx) {
 | 
						|
      // Parameter order is based on the regex group number matched.
 | 
						|
      const params = {
 | 
						|
        changeNum: ctx.params[0],
 | 
						|
        basePatchNum: ctx.params[3],
 | 
						|
        patchNum: ctx.params[5],
 | 
						|
        view: Gerrit.Nav.View.CHANGE,
 | 
						|
        querystring: ctx.querystring,
 | 
						|
      };
 | 
						|
 | 
						|
      this._normalizeLegacyRouteParams(params);
 | 
						|
    },
 | 
						|
 | 
						|
    _handleLegacyLinenum(ctx) {
 | 
						|
      this._redirect(ctx.path.replace(LEGACY_LINENUM_PATTERN, '#$1'));
 | 
						|
    },
 | 
						|
 | 
						|
    _handleDiffLegacyRoute(ctx) {
 | 
						|
      // Parameter order is based on the regex group number matched.
 | 
						|
      const params = {
 | 
						|
        changeNum: ctx.params[0],
 | 
						|
        basePatchNum: ctx.params[2],
 | 
						|
        patchNum: ctx.params[4],
 | 
						|
        path: ctx.params[5],
 | 
						|
        view: Gerrit.Nav.View.DIFF,
 | 
						|
      };
 | 
						|
 | 
						|
      const address = this._parseLineAddress(ctx.hash);
 | 
						|
      if (address) {
 | 
						|
        params.leftSide = address.leftSide;
 | 
						|
        params.lineNum = address.lineNum;
 | 
						|
      }
 | 
						|
 | 
						|
      this._normalizeLegacyRouteParams(params);
 | 
						|
    },
 | 
						|
 | 
						|
    _handleDiffEditRoute(ctx) {
 | 
						|
      // Parameter order is based on the regex group number matched.
 | 
						|
      const project = ctx.params[0];
 | 
						|
      this._redirectOrNavigate({
 | 
						|
        project,
 | 
						|
        changeNum: ctx.params[1],
 | 
						|
        patchNum: ctx.params[2],
 | 
						|
        path: ctx.params[3],
 | 
						|
        view: Gerrit.Nav.View.EDIT,
 | 
						|
      });
 | 
						|
      this.$.reporting.setRepoName(project);
 | 
						|
    },
 | 
						|
 | 
						|
    _handleChangeEditRoute(ctx) {
 | 
						|
      // Parameter order is based on the regex group number matched.
 | 
						|
      const project = ctx.params[0];
 | 
						|
      this._redirectOrNavigate({
 | 
						|
        project,
 | 
						|
        changeNum: ctx.params[1],
 | 
						|
        patchNum: ctx.params[3],
 | 
						|
        view: Gerrit.Nav.View.CHANGE,
 | 
						|
        edit: true,
 | 
						|
      });
 | 
						|
      this.$.reporting.setRepoName(project);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Normalize the patch range params for a the change or diff view and
 | 
						|
     * redirect if URL upgrade is needed.
 | 
						|
     */
 | 
						|
    _redirectOrNavigate(params) {
 | 
						|
      const needsRedirect = this._normalizePatchRangeParams(params);
 | 
						|
      if (needsRedirect) {
 | 
						|
        this._redirect(this._generateUrl(params));
 | 
						|
      } else {
 | 
						|
        this._setParams(params);
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    // TODO fix this so it properly redirects
 | 
						|
    // to /settings#Agreements (Scrolls down)
 | 
						|
    _handleAgreementsRoute(data) {
 | 
						|
      this._redirect('/settings/#Agreements');
 | 
						|
    },
 | 
						|
 | 
						|
    _handleNewAgreementsRoute(data) {
 | 
						|
      data.params.view = Gerrit.Nav.View.AGREEMENTS;
 | 
						|
      this._setParams(data.params);
 | 
						|
    },
 | 
						|
 | 
						|
    _handleSettingsLegacyRoute(data) {
 | 
						|
      // email tokens may contain '+' but no space.
 | 
						|
      // The parameter parsing replaces all '+' with a space,
 | 
						|
      // undo that to have valid tokens.
 | 
						|
      const token = data.params[0].replace(/ /g, '+');
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.SETTINGS,
 | 
						|
        emailToken: token,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleSettingsRoute(data) {
 | 
						|
      this._setParams({view: Gerrit.Nav.View.SETTINGS});
 | 
						|
    },
 | 
						|
 | 
						|
    _handleRegisterRoute(ctx) {
 | 
						|
      this._setParams({justRegistered: true});
 | 
						|
      let path = ctx.params[0] || '/';
 | 
						|
 | 
						|
      // Prevent redirect looping.
 | 
						|
      if (path.startsWith('/register')) { path = '/'; }
 | 
						|
 | 
						|
      if (path[0] !== '/') { return; }
 | 
						|
      this._redirect(this.getBaseUrl() + path);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Handler for routes that should pass through the router and not be caught
 | 
						|
     * by the catchall _handleDefaultRoute handler.
 | 
						|
     */
 | 
						|
    _handlePassThroughRoute() {
 | 
						|
      location.reload();
 | 
						|
    },
 | 
						|
 | 
						|
 | 
						|
    /**
 | 
						|
     * URL may sometimes have /+/ encoded to / /.
 | 
						|
     * Context: Issue 6888, Issue 7100
 | 
						|
     */
 | 
						|
    _handleImproperlyEncodedPlusRoute(ctx) {
 | 
						|
      let hash = this._getHashFromCanonicalPath(ctx.canonicalPath);
 | 
						|
      if (hash.length) { hash = '#' + hash; }
 | 
						|
      this._redirect(`/c/${ctx.params[0]}/+/${ctx.params[1]}${hash}`);
 | 
						|
    },
 | 
						|
 | 
						|
    _handlePluginScreen(ctx) {
 | 
						|
      const view = Gerrit.Nav.View.PLUGIN_SCREEN;
 | 
						|
      const plugin = ctx.params[0];
 | 
						|
      const screen = ctx.params[1];
 | 
						|
      this._setParams({view, plugin, screen});
 | 
						|
    },
 | 
						|
 | 
						|
    _handleDocumentationSearchRoute(data) {
 | 
						|
      this._setParams({
 | 
						|
        view: Gerrit.Nav.View.DOCUMENTATION_SEARCH,
 | 
						|
        filter: data.params.filter || null,
 | 
						|
      });
 | 
						|
    },
 | 
						|
 | 
						|
    _handleDocumentationSearchRedirectRoute(data) {
 | 
						|
      this._redirect('/Documentation/q/filter:' +
 | 
						|
          encodeURIComponent(data.params[0]));
 | 
						|
    },
 | 
						|
 | 
						|
    _handleDocumentationRedirectRoute(data) {
 | 
						|
      if (data.params[1]) {
 | 
						|
        location.reload();
 | 
						|
      } else {
 | 
						|
        // Redirect /Documentation to /Documentation/index.html
 | 
						|
        this._redirect('/Documentation/index.html');
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Catchall route for when no other route is matched.
 | 
						|
     */
 | 
						|
    _handleDefaultRoute() {
 | 
						|
      if (this._isInitialLoad) {
 | 
						|
        // Server recognized this route as polygerrit, so we show 404.
 | 
						|
        this._show404();
 | 
						|
      } else {
 | 
						|
        // Route can be recognized by server, so we pass it to server.
 | 
						|
        this._handlePassThroughRoute();
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    _show404() {
 | 
						|
      // Note: the app's 404 display is tightly-coupled with catching 404
 | 
						|
      // network responses, so we simulate a 404 response status to display it.
 | 
						|
      // TODO: Decouple the gr-app error view from network responses.
 | 
						|
      this._appElement().dispatchEvent(new CustomEvent('page-error',
 | 
						|
          {detail: {response: {status: 404}}}));
 | 
						|
    },
 | 
						|
  });
 | 
						|
})();
 |