|  | /** | 
|  | * @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 PREFS_SECTION_FIELDS = [ | 
|  | 'changes_per_page', | 
|  | 'date_format', | 
|  | 'time_format', | 
|  | 'email_strategy', | 
|  | 'diff_view', | 
|  | 'publish_comments_on_push', | 
|  | 'work_in_progress_by_default', | 
|  | 'default_base_for_merges', | 
|  | 'signed_off_by', | 
|  | 'email_format', | 
|  | 'size_bar_in_change_table', | 
|  | 'relative_date_in_change_table', | 
|  | ]; | 
|  |  | 
|  | const GERRIT_DOCS_BASE_URL = 'https://gerrit-review.googlesource.com/' + | 
|  | 'Documentation'; | 
|  | const GERRIT_DOCS_FILTER_PATH = '/user-notify.html'; | 
|  | const ABSOLUTE_URL_PATTERN = /^https?:/; | 
|  | const TRAILING_SLASH_PATTERN = /\/$/; | 
|  |  | 
|  | const RELOAD_MESSAGE = 'Reloading...'; | 
|  |  | 
|  | const HTTP_AUTH = [ | 
|  | 'HTTP', | 
|  | 'HTTP_LDAP', | 
|  | ]; | 
|  |  | 
|  | /** | 
|  | * @appliesMixin Gerrit.DocsUrlMixin | 
|  | * @appliesMixin Gerrit.ChangeTableMixin | 
|  | * @appliesMixin Gerrit.FireMixin | 
|  | * @extends Polymer.Element | 
|  | */ | 
|  | class GrSettingsView extends Polymer.mixinBehaviors( [ | 
|  | Gerrit.DocsUrlBehavior, | 
|  | Gerrit.ChangeTableBehavior, | 
|  | Gerrit.FireBehavior, | 
|  | ], Polymer.GestureEventListeners( | 
|  | Polymer.LegacyElementMixin( | 
|  | Polymer.Element))) { | 
|  | static get is() { return 'gr-settings-view'; } | 
|  | /** | 
|  | * Fired when the title of the page should change. | 
|  | * | 
|  | * @event title-change | 
|  | */ | 
|  |  | 
|  | /** | 
|  | * Fired with email confirmation text, or when the page reloads. | 
|  | * | 
|  | * @event show-alert | 
|  | */ | 
|  |  | 
|  | static get properties() { | 
|  | return { | 
|  | prefs: { | 
|  | type: Object, | 
|  | value() { return {}; }, | 
|  | }, | 
|  | params: { | 
|  | type: Object, | 
|  | value() { return {}; }, | 
|  | }, | 
|  | _accountInfoChanged: Boolean, | 
|  | _changeTableColumnsNotDisplayed: Array, | 
|  | /** @type {?} */ | 
|  | _localPrefs: { | 
|  | type: Object, | 
|  | value() { return {}; }, | 
|  | }, | 
|  | _localChangeTableColumns: { | 
|  | type: Array, | 
|  | value() { return []; }, | 
|  | }, | 
|  | _localMenu: { | 
|  | type: Array, | 
|  | value() { return []; }, | 
|  | }, | 
|  | _loading: { | 
|  | type: Boolean, | 
|  | value: true, | 
|  | }, | 
|  | _changeTableChanged: { | 
|  | type: Boolean, | 
|  | value: false, | 
|  | }, | 
|  | _prefsChanged: { | 
|  | type: Boolean, | 
|  | value: false, | 
|  | }, | 
|  | /** @type {?} */ | 
|  | _diffPrefsChanged: Boolean, | 
|  | /** @type {?} */ | 
|  | _editPrefsChanged: Boolean, | 
|  | _menuChanged: { | 
|  | type: Boolean, | 
|  | value: false, | 
|  | }, | 
|  | _watchedProjectsChanged: { | 
|  | type: Boolean, | 
|  | value: false, | 
|  | }, | 
|  | _keysChanged: { | 
|  | type: Boolean, | 
|  | value: false, | 
|  | }, | 
|  | _gpgKeysChanged: { | 
|  | type: Boolean, | 
|  | value: false, | 
|  | }, | 
|  | _newEmail: String, | 
|  | _addingEmail: { | 
|  | type: Boolean, | 
|  | value: false, | 
|  | }, | 
|  | _lastSentVerificationEmail: { | 
|  | type: String, | 
|  | value: null, | 
|  | }, | 
|  | /** @type {?} */ | 
|  | _serverConfig: Object, | 
|  | /** @type {?string} */ | 
|  | _docsBaseUrl: String, | 
|  | _emailsChanged: Boolean, | 
|  |  | 
|  | /** | 
|  | * For testing purposes. | 
|  | */ | 
|  | _loadingPromise: Object, | 
|  |  | 
|  | _showNumber: Boolean, | 
|  |  | 
|  | _isDark: { | 
|  | type: Boolean, | 
|  | value: false, | 
|  | }, | 
|  | }; | 
|  | } | 
|  |  | 
|  | static get observers() { | 
|  | return [ | 
|  | '_handlePrefsChanged(_localPrefs.*)', | 
|  | '_handleMenuChanged(_localMenu.splices)', | 
|  | '_handleChangeTableChanged(_localChangeTableColumns, _showNumber)', | 
|  | ]; | 
|  | } | 
|  |  | 
|  | /** @override */ | 
|  | attached() { | 
|  | super.attached(); | 
|  | // Polymer 2: anchor tag won't work on shadow DOM | 
|  | // we need to manually calling scrollIntoView when hash changed | 
|  | this.listen(window, 'location-change', '_handleLocationChange'); | 
|  | this.fire('title-change', {title: 'Settings'}); | 
|  |  | 
|  | this._isDark = !!window.localStorage.getItem('dark-theme'); | 
|  |  | 
|  | const promises = [ | 
|  | this.$.accountInfo.loadData(), | 
|  | this.$.watchedProjectsEditor.loadData(), | 
|  | this.$.groupList.loadData(), | 
|  | this.$.identities.loadData(), | 
|  | this.$.editPrefs.loadData(), | 
|  | this.$.diffPrefs.loadData(), | 
|  | ]; | 
|  |  | 
|  | promises.push(this.$.restAPI.getPreferences().then(prefs => { | 
|  | this.prefs = prefs; | 
|  | this._showNumber = !!prefs.legacycid_in_change_table; | 
|  | this._copyPrefs('_localPrefs', 'prefs'); | 
|  | this._cloneMenu(prefs.my); | 
|  | this._cloneChangeTableColumns(); | 
|  | })); | 
|  |  | 
|  | promises.push(this.$.restAPI.getConfig().then(config => { | 
|  | this._serverConfig = config; | 
|  | const configPromises = []; | 
|  |  | 
|  | if (this._serverConfig && this._serverConfig.sshd) { | 
|  | configPromises.push(this.$.sshEditor.loadData()); | 
|  | } | 
|  |  | 
|  | if (this._serverConfig && | 
|  | this._serverConfig.receive && | 
|  | this._serverConfig.receive.enable_signed_push) { | 
|  | configPromises.push(this.$.gpgEditor.loadData()); | 
|  | } | 
|  |  | 
|  | configPromises.push( | 
|  | this.getDocsBaseUrl(config, this.$.restAPI) | 
|  | .then(baseUrl => { this._docsBaseUrl = baseUrl; })); | 
|  |  | 
|  | return Promise.all(configPromises); | 
|  | })); | 
|  |  | 
|  | if (this.params.emailToken) { | 
|  | promises.push(this.$.restAPI.confirmEmail(this.params.emailToken).then( | 
|  | message => { | 
|  | if (message) { | 
|  | this.fire('show-alert', {message}); | 
|  | } | 
|  | this.$.emailEditor.loadData(); | 
|  | })); | 
|  | } else { | 
|  | promises.push(this.$.emailEditor.loadData()); | 
|  | } | 
|  |  | 
|  | this._loadingPromise = Promise.all(promises).then(() => { | 
|  | this._loading = false; | 
|  |  | 
|  | // Handle anchor tag for initial load | 
|  | this._handleLocationChange(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | /** @override */ | 
|  | detached() { | 
|  | super.detached(); | 
|  | this.unlisten(window, 'location-change', '_handleLocationChange'); | 
|  | } | 
|  |  | 
|  | _handleLocationChange() { | 
|  | // Handle anchor tag after dom attached | 
|  | const urlHash = window.location.hash; | 
|  | if (urlHash) { | 
|  | // Use shadowRoot for Polymer 2 | 
|  | const elem = (this.shadowRoot || document).querySelector(urlHash); | 
|  | if (elem) { | 
|  | elem.scrollIntoView(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | reloadAccountDetail() { | 
|  | Promise.all([ | 
|  | this.$.accountInfo.loadData(), | 
|  | this.$.emailEditor.loadData(), | 
|  | ]); | 
|  | } | 
|  |  | 
|  | _isLoading() { | 
|  | return this._loading || this._loading === undefined; | 
|  | } | 
|  |  | 
|  | _copyPrefs(to, from) { | 
|  | for (let i = 0; i < PREFS_SECTION_FIELDS.length; i++) { | 
|  | this.set([to, PREFS_SECTION_FIELDS[i]], | 
|  | this[from][PREFS_SECTION_FIELDS[i]]); | 
|  | } | 
|  | } | 
|  |  | 
|  | _cloneMenu(prefs) { | 
|  | const menu = []; | 
|  | for (const item of prefs) { | 
|  | menu.push({ | 
|  | name: item.name, | 
|  | url: item.url, | 
|  | target: item.target, | 
|  | }); | 
|  | } | 
|  | this._localMenu = menu; | 
|  | } | 
|  |  | 
|  | _cloneChangeTableColumns() { | 
|  | let columns = this.getVisibleColumns(this.prefs.change_table); | 
|  |  | 
|  | if (columns.length === 0) { | 
|  | columns = this.columnNames; | 
|  | this._changeTableColumnsNotDisplayed = []; | 
|  | } else { | 
|  | this._changeTableColumnsNotDisplayed = this.getComplementColumns( | 
|  | this.prefs.change_table); | 
|  | } | 
|  | this._localChangeTableColumns = columns; | 
|  | } | 
|  |  | 
|  | _formatChangeTableColumns(changeTableArray) { | 
|  | return changeTableArray.map(item => { | 
|  | return {column: item}; | 
|  | }); | 
|  | } | 
|  |  | 
|  | _handleChangeTableChanged() { | 
|  | if (this._isLoading()) { return; } | 
|  | this._changeTableChanged = true; | 
|  | } | 
|  |  | 
|  | _handlePrefsChanged(prefs) { | 
|  | if (this._isLoading()) { return; } | 
|  | this._prefsChanged = true; | 
|  | } | 
|  |  | 
|  | _handleRelativeDateInChangeTable() { | 
|  | this.set('_localPrefs.relative_date_in_change_table', | 
|  | this.$.relativeDateInChangeTable.checked); | 
|  | } | 
|  |  | 
|  | _handleShowSizeBarsInFileListChanged() { | 
|  | this.set('_localPrefs.size_bar_in_change_table', | 
|  | this.$.showSizeBarsInFileList.checked); | 
|  | } | 
|  |  | 
|  | _handlePublishCommentsOnPushChanged() { | 
|  | this.set('_localPrefs.publish_comments_on_push', | 
|  | this.$.publishCommentsOnPush.checked); | 
|  | } | 
|  |  | 
|  | _handleWorkInProgressByDefault() { | 
|  | this.set('_localPrefs.work_in_progress_by_default', | 
|  | this.$.workInProgressByDefault.checked); | 
|  | } | 
|  |  | 
|  | _handleInsertSignedOff() { | 
|  | this.set('_localPrefs.signed_off_by', this.$.insertSignedOff.checked); | 
|  | } | 
|  |  | 
|  | _handleMenuChanged() { | 
|  | if (this._isLoading()) { return; } | 
|  | this._menuChanged = true; | 
|  | } | 
|  |  | 
|  | _handleSaveAccountInfo() { | 
|  | this.$.accountInfo.save(); | 
|  | } | 
|  |  | 
|  | _handleSavePreferences() { | 
|  | this._copyPrefs('prefs', '_localPrefs'); | 
|  |  | 
|  | return this.$.restAPI.savePreferences(this.prefs).then(() => { | 
|  | this._prefsChanged = false; | 
|  | }); | 
|  | } | 
|  |  | 
|  | _handleSaveChangeTable() { | 
|  | this.set('prefs.change_table', this._localChangeTableColumns); | 
|  | this.set('prefs.legacycid_in_change_table', this._showNumber); | 
|  | this._cloneChangeTableColumns(); | 
|  | return this.$.restAPI.savePreferences(this.prefs).then(() => { | 
|  | this._changeTableChanged = false; | 
|  | }); | 
|  | } | 
|  |  | 
|  | _handleSaveDiffPreferences() { | 
|  | this.$.diffPrefs.save(); | 
|  | } | 
|  |  | 
|  | _handleSaveEditPreferences() { | 
|  | this.$.editPrefs.save(); | 
|  | } | 
|  |  | 
|  | _handleSaveMenu() { | 
|  | this.set('prefs.my', this._localMenu); | 
|  | this._cloneMenu(this.prefs.my); | 
|  | return this.$.restAPI.savePreferences(this.prefs).then(() => { | 
|  | this._menuChanged = false; | 
|  | }); | 
|  | } | 
|  |  | 
|  | _handleResetMenuButton() { | 
|  | return this.$.restAPI.getDefaultPreferences().then(data => { | 
|  | if (data && data.my) { | 
|  | this._cloneMenu(data.my); | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | _handleSaveWatchedProjects() { | 
|  | this.$.watchedProjectsEditor.save(); | 
|  | } | 
|  |  | 
|  | _computeHeaderClass(changed) { | 
|  | return changed ? 'edited' : ''; | 
|  | } | 
|  |  | 
|  | _handleSaveEmails() { | 
|  | this.$.emailEditor.save(); | 
|  | } | 
|  |  | 
|  | _handleNewEmailKeydown(e) { | 
|  | if (e.keyCode === 13) { // Enter | 
|  | e.stopPropagation(); | 
|  | this._handleAddEmailButton(); | 
|  | } | 
|  | } | 
|  |  | 
|  | _isNewEmailValid(newEmail) { | 
|  | return newEmail && newEmail.includes('@'); | 
|  | } | 
|  |  | 
|  | _computeAddEmailButtonEnabled(newEmail, addingEmail) { | 
|  | return this._isNewEmailValid(newEmail) && !addingEmail; | 
|  | } | 
|  |  | 
|  | _handleAddEmailButton() { | 
|  | if (!this._isNewEmailValid(this._newEmail)) { return; } | 
|  |  | 
|  | this._addingEmail = true; | 
|  | this.$.restAPI.addAccountEmail(this._newEmail).then(response => { | 
|  | this._addingEmail = false; | 
|  |  | 
|  | // If it was unsuccessful. | 
|  | if (response.status < 200 || response.status >= 300) { return; } | 
|  |  | 
|  | this._lastSentVerificationEmail = this._newEmail; | 
|  | this._newEmail = ''; | 
|  | }); | 
|  | } | 
|  |  | 
|  | _getFilterDocsLink(docsBaseUrl) { | 
|  | let base = docsBaseUrl; | 
|  | if (!base || !ABSOLUTE_URL_PATTERN.test(base)) { | 
|  | base = GERRIT_DOCS_BASE_URL; | 
|  | } | 
|  |  | 
|  | // Remove any trailing slash, since it is in the GERRIT_DOCS_FILTER_PATH. | 
|  | base = base.replace(TRAILING_SLASH_PATTERN, ''); | 
|  |  | 
|  | return base + GERRIT_DOCS_FILTER_PATH; | 
|  | } | 
|  |  | 
|  | _handleToggleDark() { | 
|  | if (this._isDark) { | 
|  | window.localStorage.removeItem('dark-theme'); | 
|  | } else { | 
|  | window.localStorage.setItem('dark-theme', 'true'); | 
|  | } | 
|  | this.dispatchEvent(new CustomEvent('show-alert', { | 
|  | detail: {message: RELOAD_MESSAGE}, | 
|  | bubbles: true, | 
|  | composed: true, | 
|  | })); | 
|  | this.async(() => { | 
|  | window.location.reload(); | 
|  | }, 1); | 
|  | } | 
|  |  | 
|  | _showHttpAuth(config) { | 
|  | if (config && config.auth && | 
|  | config.auth.git_basic_auth_policy) { | 
|  | return HTTP_AUTH.includes( | 
|  | config.auth.git_basic_auth_policy.toUpperCase()); | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | customElements.define(GrSettingsView.is, GrSettingsView); | 
|  | })(); |