Convert polygerrit to es6-modules
This change replace all HTML imports with es6-modules. The only exceptions are:
* gr-app.html file, which can be deleted only after updating the
gerrit/httpd/raw/PolyGerritIndexHtml.soy file.
* dark-theme.html which is loaded via importHref. Must be updated manually
later in a separate change.
This change was produced automatically by ./es6-modules-converter.sh script.
No manual changes were made.
Change-Id: I0c447dd8c05757741e2c940720652d01d9fb7d67
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
index ebac1e1..e461d1d 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -14,1520 +14,1535 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-(function() {
- 'use strict';
+import '../../../scripts/bundled-polymer.js';
- const RoutePattern = {
- ROOT: '/',
+import '../../../behaviors/base-url-behavior/base-url-behavior.js';
+import '../../../behaviors/fire-behavior/fire-behavior.js';
+import '../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.js';
+import '../../../behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior.js';
+import '../gr-navigation/gr-navigation.js';
+import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
+import '../gr-reporting/gr-reporting.js';
+import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js';
+import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
+import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin.js';
+import {PolymerElement} from '@polymer/polymer/polymer-element.js';
+import page from 'page/page.mjs';
+self.page = page;
+import {htmlTemplate} from './gr-router_html.js';
- DASHBOARD: /^\/dashboard\/(.+)$/,
- CUSTOM_DASHBOARD: /^\/dashboard\/?$/,
- PROJECT_DASHBOARD: /^\/p\/(.+)\/\+\/dashboard\/(.+)/,
+const RoutePattern = {
+ ROOT: '/',
- AGREEMENTS: /^\/settings\/agreements\/?/,
- NEW_AGREEMENTS: /^\/settings\/new-agreement\/?/,
- REGISTER: /^\/register(\/.*)?$/,
+ DASHBOARD: /^\/dashboard\/(.+)$/,
+ CUSTOM_DASHBOARD: /^\/dashboard\/?$/,
+ PROJECT_DASHBOARD: /^\/p\/(.+)\/\+\/dashboard\/(.+)/,
- // Pattern for login and logout URLs intended to be passed-through. May
- // include a return URL.
- LOG_IN_OR_OUT: /\/log(in|out)(\/(.+))?$/,
+ AGREEMENTS: /^\/settings\/agreements\/?/,
+ NEW_AGREEMENTS: /^\/settings\/new-agreement\/?/,
+ REGISTER: /^\/register(\/.*)?$/,
- // Pattern for a catchall route when no other pattern is matched.
- DEFAULT: /.*/,
+ // Pattern for login and logout URLs intended to be passed-through. May
+ // include a return URL.
+ LOG_IN_OR_OUT: /\/log(in|out)(\/(.+))?$/,
- // Matches /admin/groups/[uuid-]<group>
- GROUP: /^\/admin\/groups\/(?:uuid-)?([^,]+)$/,
+ // Pattern for a catchall route when no other pattern is matched.
+ DEFAULT: /.*/,
- // Redirects /groups/self to /settings/#Groups for GWT compatibility
- GROUP_SELF: /^\/groups\/self/,
+ // Matches /admin/groups/[uuid-]<group>
+ GROUP: /^\/admin\/groups\/(?:uuid-)?([^,]+)$/,
- // Matches /admin/groups/[uuid-]<group>,info (backwords compat with gwtui)
- // Redirects to /admin/groups/[uuid-]<group>
- GROUP_INFO: /^\/admin\/groups\/(?:uuid-)?(.+),info$/,
+ // Redirects /groups/self to /settings/#Groups for GWT compatibility
+ GROUP_SELF: /^\/groups\/self/,
- // Matches /admin/groups/<group>,audit-log
- GROUP_AUDIT_LOG: /^\/admin\/groups\/(?:uuid-)?(.+),audit-log$/,
+ // 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/[uuid-]<group>,members
- GROUP_MEMBERS: /^\/admin\/groups\/(?:uuid-)?(.+),members$/,
+ // Matches /admin/groups/<group>,audit-log
+ GROUP_AUDIT_LOG: /^\/admin\/groups\/(?:uuid-)?(.+),audit-log$/,
- // 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/groups/[uuid-]<group>,members
+ GROUP_MEMBERS: /^\/admin\/groups\/(?:uuid-)?(.+),members$/,
- // Matches /admin/create-project
- LEGACY_CREATE_PROJECT: /^\/admin\/create-project\/?$/,
+ // 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_GROUP: /^\/admin\/create-group\/?$/,
+ // Matches /admin/create-project
+ LEGACY_CREATE_PROJECT: /^\/admin\/create-project\/?$/,
- PROJECT_OLD: /^\/admin\/(projects)\/?(.+)?$/,
+ // Matches /admin/create-project
+ LEGACY_CREATE_GROUP: /^\/admin\/create-group\/?$/,
- // Matches /admin/repos/<repo>
- REPO: /^\/admin\/repos\/([^,]+)$/,
+ PROJECT_OLD: /^\/admin\/(projects)\/?(.+)?$/,
- // Matches /admin/repos/<repo>,commands.
- REPO_COMMANDS: /^\/admin\/repos\/(.+),commands$/,
+ // Matches /admin/repos/<repo>
+ REPO: /^\/admin\/repos\/([^,]+)$/,
- // Matches /admin/repos/<repos>,access.
- REPO_ACCESS: /^\/admin\/repos\/(.+),access$/,
+ // Matches /admin/repos/<repo>,commands.
+ REPO_COMMANDS: /^\/admin\/repos\/(.+),commands$/,
- // Matches /admin/repos/<repos>,access.
- REPO_DASHBOARDS: /^\/admin\/repos\/(.+),dashboards$/,
+ // Matches /admin/repos/<repos>,access.
+ REPO_ACCESS: /^\/admin\/repos\/(.+),access$/,
- // 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/<repos>,access.
+ REPO_DASHBOARDS: /^\/admin\/repos\/(.+),dashboards$/,
- // 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[,<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>,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',
+ // 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',
- PLUGINS: /^\/plugins\/(.+)$/,
+ // 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',
- PLUGIN_LIST: /^\/admin\/plugins(\/)?$/,
+ PLUGINS: /^\/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',
+ PLUGIN_LIST: /^\/admin\/plugins(\/)?$/,
- QUERY: /^\/q\/([^,]+)(,(\d+))?$/,
+ // 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',
- /**
- * 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[#lineNum]
- DIFF_EDIT: /^\/c\/(.+)\/\+\/(\d+)\/(\d+|edit)\/(.+),edit(\#\d+)?$/,
-
- // 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(\/)?(.+)?/,
- };
+ QUERY: /^\/q\/([^,]+)(,(\d+))?$/,
/**
- * 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.
+ * Support vestigial params from GWT UI.
*
- * @type {RegExp}
+ * @see Issue 7673.
+ * @type {!RegExp}
*/
- const LINE_ADDRESS_PATTERN = /^([ab]?)(\d+)$/;
+ QUERY_LEGACY_SUFFIX: /^\/q\/.+,n,z$/,
- /**
- * Pattern to recognize '+' in url-encoded strings for replacement with ' '.
- */
- const PLUS_PATTERN = /\+/g;
+ // Matches /c/<changeNum>/[<basePatchNum>..][<patchNum>][/].
+ CHANGE_LEGACY: /^\/c\/(\d+)\/?(((-?\d+|edit)(\.\.(\d+|edit))?))?\/?$/,
+ CHANGE_NUMBER_LEGACY: /^\/(\d+)\/?/,
- /**
- * Pattern to recognize leading '?' in window.location.search, for stripping.
- */
- const QUESTION_PATTERN = /^\?*/;
+ // 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))?))?\/?$/,
- /**
- * GWT UI would use @\d+ at the end of a path to indicate linenum.
- */
- const LEGACY_LINENUM_PATTERN = /@([ab]?\d+)$/;
+ // Matches /c/<project>/+/<changeNum>/[<patchNum|edit>],edit
+ CHANGE_EDIT: /^\/c\/(.+)\/\+\/(\d+)(\/(\d+))?,edit\/?$/,
- const LEGACY_QUERY_SUFFIX_PATTERN = /,n,z$/;
+ // 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))?(\/(.+))))\/?$/,
- const REPO_TOKEN_PATTERN = /\$\{(project|repo)\}/g;
+ // Matches /c/<project>/+/<changeNum>/[<patchNum|edit>]/<path>,edit[#lineNum]
+ DIFF_EDIT: /^\/c\/(.+)\/\+\/(\d+)\/(\d+|edit)\/(.+),edit(\#\d+)?$/,
- // 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)');
+ // 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('WebComponentsReady', () => {
+ reporting.timeEnd('WebComponentsReady');
+ });
+})();
+
+/**
+ * @appliesMixin Gerrit.BaseUrlMixin
+ * @appliesMixin Gerrit.FireMixin
+ * @appliesMixin Gerrit.PatchSetMixin
+ * @appliesMixin Gerrit.URLEncodingMixin
+ * @extends Polymer.Element
+ */
+class GrRouter extends mixinBehaviors( [
+ Gerrit.BaseUrlBehavior,
+ Gerrit.FireBehavior,
+ Gerrit.PatchSetBehavior,
+ Gerrit.URLEncodingBehavior,
+], GestureEventListeners(
+ LegacyElementMixin(
+ PolymerElement))) {
+ static get template() { return htmlTemplate; }
+
+ static get is() { return 'gr-router'; }
+
+ static get properties() {
+ return {
+ _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,
+ },
+ };
}
- // Setup listeners outside of the router component initialization.
- (function() {
- const reporting = document.createElement('gr-reporting');
+ start() {
+ if (!this._app) { return; }
+ this._startRouter();
+ }
- window.addEventListener('WebComponentsReady', () => {
- reporting.timeEnd('WebComponentsReady');
- });
- })();
+ _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);
+ }
/**
- * @appliesMixin Gerrit.BaseUrlMixin
- * @appliesMixin Gerrit.FireMixin
- * @appliesMixin Gerrit.PatchSetMixin
- * @appliesMixin Gerrit.URLEncodingMixin
- * @extends Polymer.Element
+ * @param {!Object} params
+ * @return {string}
*/
- class GrRouter extends Polymer.mixinBehaviors( [
- Gerrit.BaseUrlBehavior,
- Gerrit.FireBehavior,
- Gerrit.PatchSetBehavior,
- Gerrit.URLEncodingBehavior,
- ], Polymer.GestureEventListeners(
- Polymer.LegacyElementMixin(
- Polymer.Element))) {
- static get is() { return 'gr-router'; }
+ _generateUrl(params) {
+ const base = this.getBaseUrl();
+ let url = '';
+ const Views = Gerrit.Nav.View;
- static get properties() {
- return {
- _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,
- },
- };
+ 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');
}
- start() {
- if (!this._app) { return; }
- this._startRouter();
+ 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;
}
- _setParams(params) {
- this._appElement().params = params;
+ if (params.query) {
+ return '/q/' + this.encodeURL(params.query, true) + offsetExpr;
}
- _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');
+ 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 ') +
+ ')');
+ }
}
- _redirect(url) {
- this._isRedirecting = true;
- page.redirect(url);
+ 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;
}
- /**
- * @param {!Object} params
- * @return {string}
- */
- _generateUrl(params) {
- const base = this.getBaseUrl();
- let url = '';
- const Views = Gerrit.Nav.View;
+ if (params.project) {
+ const encodedProject = this.encodeURL(params.project, true);
+ return `/c/${encodedProject}/+/${params.changeNum}${suffix}`;
+ } else {
+ return `/c/${params.changeNum}${suffix}`;
+ }
+ }
- 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);
+ /**
+ * @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 {
- throw new Error('Can\'t generate');
+ this._redirectToLogin(data.canonicalPath);
+ return Promise.reject(new Error());
}
+ });
+ }
- return base + url;
+ /** 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);
}
- _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}!`);
+ 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();
+ });
- _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};
- }
- }
+ // Middleware
+ page((ctx, next) => {
+ document.body.scrollTop = 0;
- _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);
+ 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;
}
- 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() {
+ // 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();
- if (base) {
- page.base(base);
+ let newUrl = base + hash;
+ if (hash.startsWith('/VE/')) {
+ newUrl = base + '/settings' + hash;
}
-
- 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();
+ this._redirect(newUrl);
+ return null;
}
+ return this.$.restAPI.getLoggedIn().then(loggedIn => {
+ if (loggedIn) {
+ this._redirect('/dashboard/self');
+ } else {
+ this._redirect('/q/status:open');
+ }
+ });
+ }
- /**
- * @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;
+ /**
+ * 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));
}
- 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;
+ if (name) {
+ params.push([name, value]);
}
- return this.$.restAPI.getLoggedIn().then(loggedIn => {
- if (loggedIn) {
- this._redirect('/dashboard/self');
+ });
+ 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/status:open');
+ this._redirect('/q/owner:' + encodeURIComponent(data.params[0]));
}
- });
- }
-
- /**
- * 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.
+ } else {
this._setParams({
view: Gerrit.Nav.View.DASHBOARD,
- user: 'self',
- sections,
- title,
+ user: data.params[0],
});
- return Promise.resolve();
}
+ });
+ }
- // Redirect /dashboard/ -> /dashboard/self.
- this._redirect('/dashboard/self');
+ /**
+ * 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();
}
- _handleProjectDashboardRoute(data) {
- const project = data.params[0];
- this._setParams({
- view: Gerrit.Nav.View.DASHBOARD,
- project,
- dashboard: decodeURIComponent(data.params[1]),
- });
- this.$.reporting.setRepoName(project);
- }
+ // Redirect /dashboard/ -> /dashboard/self.
+ this._redirect('/dashboard/self');
+ return Promise.resolve();
+ }
- _handleGroupInfoRoute(data) {
- this._redirect('/admin/groups/' + encodeURIComponent(data.params[0]));
- }
+ _handleProjectDashboardRoute(data) {
+ const project = data.params[0];
+ this._setParams({
+ view: Gerrit.Nav.View.DASHBOARD,
+ project,
+ dashboard: decodeURIComponent(data.params[1]),
+ });
+ this.$.reporting.setRepoName(project);
+ }
- _handleGroupSelfRedirectRoute(data) {
- this._redirect('/settings/#Groups');
- }
+ _handleGroupInfoRoute(data) {
+ this._redirect('/admin/groups/' + encodeURIComponent(data.params[0]));
+ }
- _handleGroupRoute(data) {
- this._setParams({
- view: Gerrit.Nav.View.GROUP,
- groupId: data.params[0],
- });
- }
+ _handleGroupSelfRedirectRoute(data) {
+ this._redirect('/settings/#Groups');
+ }
- _handleGroupAuditLogRoute(data) {
- this._setParams({
- view: Gerrit.Nav.View.GROUP,
- detail: Gerrit.Nav.GroupDetailView.LOG,
- groupId: data.params[0],
- });
- }
+ _handleGroupRoute(data) {
+ this._setParams({
+ view: Gerrit.Nav.View.GROUP,
+ groupId: data.params[0],
+ });
+ }
- _handleGroupMembersRoute(data) {
- this._setParams({
- view: Gerrit.Nav.View.GROUP,
- detail: Gerrit.Nav.GroupDetailView.MEMBERS,
- groupId: data.params[0],
- });
- }
+ _handleGroupAuditLogRoute(data) {
+ this._setParams({
+ view: Gerrit.Nav.View.GROUP,
+ detail: Gerrit.Nav.GroupDetailView.LOG,
+ 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',
- });
- }
+ _handleGroupMembersRoute(data) {
+ this._setParams({
+ view: Gerrit.Nav.View.GROUP,
+ detail: Gerrit.Nav.GroupDetailView.MEMBERS,
+ groupId: data.params[0],
+ });
+ }
- _handleGroupListFilterOffsetRoute(data) {
- this._setParams({
- view: Gerrit.Nav.View.ADMIN,
- adminView: 'gr-admin-group-list',
- offset: data.params.offset,
- filter: data.params.filter,
- });
- }
+ _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',
+ });
+ }
- _handleGroupListFilterRoute(data) {
- this._setParams({
- view: Gerrit.Nav.View.ADMIN,
- adminView: 'gr-admin-group-list',
- filter: data.params.filter || null,
- });
- }
+ _handleGroupListFilterOffsetRoute(data) {
+ this._setParams({
+ view: Gerrit.Nav.View.ADMIN,
+ adminView: 'gr-admin-group-list',
+ offset: data.params.offset,
+ filter: data.params.filter,
+ });
+ }
- _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', ',');
- }
- }
+ _handleGroupListFilterRoute(data) {
+ this._setParams({
+ view: Gerrit.Nav.View.ADMIN,
+ adminView: 'gr-admin-group-list',
+ filter: data.params.filter || null,
+ });
+ }
- 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],
- lineNum: ctx.hash,
- 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);
+ _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', ',');
}
}
- _handleAgreementsRoute() {
- this._redirect('/settings/#Agreements');
+ 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;
}
- _handleNewAgreementsRoute(data) {
- data.params.view = Gerrit.Nav.View.AGREEMENTS;
- this._setParams(data.params);
- }
+ this._normalizeLegacyRouteParams(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,
- });
- }
+ _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],
+ lineNum: ctx.hash,
+ view: Gerrit.Nav.View.EDIT,
+ });
+ this.$.reporting.setRepoName(project);
+ }
- _handleSettingsRoute(data) {
- this._setParams({view: Gerrit.Nav.View.SETTINGS});
- }
+ _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);
+ }
- _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}}}));
+ /**
+ * 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);
}
}
- customElements.define(GrRouter.is, GrRouter);
-})();
+ _handleAgreementsRoute() {
+ 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}}}));
+ }
+}
+
+customElements.define(GrRouter.is, GrRouter);