text: /**
   * @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 NUMBER_FIXED_COLUMNS = 3;
    const CLOSED_STATUS = ['MERGED', 'ABANDONED'];
    const LABEL_PREFIX_INVALID_PROLOG = 'Invalid-Prolog-Rules-Label-Name--';
    const MAX_SHORTCUT_CHARS = 5;
  
    Polymer({
      is: 'gr-change-list',
  
      /**
       * Fired when next page key shortcut was pressed.
       *
       * @event next-page
       */
  
      /**
       * Fired when previous page key shortcut was pressed.
       *
       * @event previous-page
       */
  
      hostAttributes: {
        tabindex: 0,
      },
  
      properties: {
        /**
         * The logged-in user's account, or an empty object if no user is logged
         * in.
         */
        account: {
          type: Object,
          value: null,
        },
        /**
         * An array of ChangeInfo objects to render.
         * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#change-info
         */
        changes: {
          type: Array,
          observer: '_changesChanged',
        },
        /**
         * ChangeInfo objects grouped into arrays. The sections and changes
         * properties should not be used together.
         *
         * @type {!Array<{
         *   name: string,
         *   query: string,
         *   results: !Array<!Object>
         * }>}
         */
        sections: {
          type: Array,
          value() { return []; },
        },
        labelNames: {
          type: Array,
          computed: '_computeLabelNames(sections)',
        },
        _dynamicHeaderEndpoints: {
          type: Array,
        },
        selectedIndex: {
          type: Number,
          notify: true,
        },
        showNumber: Boolean, // No default value to prevent flickering.
        showStar: {
          type: Boolean,
          value: false,
        },
        showReviewedState: {
          type: Boolean,
          value: false,
        },
        keyEventTarget: {
          type: Object,
          value() { return document.body; },
        },
        changeTableColumns: Array,
        visibleChangeTableColumns: Array,
        preferences: Object,
      },
  
      behaviors: [
        Gerrit.BaseUrlBehavior,
        Gerrit.ChangeTableBehavior,
        Gerrit.FireBehavior,
        Gerrit.KeyboardShortcutBehavior,
        Gerrit.RESTClientBehavior,
        Gerrit.URLEncodingBehavior,
      ],
  
      listeners: {
        keydown: '_scopedKeydownHandler',
      },
  
      observers: [
        '_sectionsChanged(sections.*)',
        '_computePreferences(account, preferences)',
      ],
  
      keyboardShortcuts() {
        return {
          [this.Shortcut.CURSOR_NEXT_CHANGE]: '_nextChange',
          [this.Shortcut.CURSOR_PREV_CHANGE]: '_prevChange',
          [this.Shortcut.NEXT_PAGE]: '_nextPage',
          [this.Shortcut.PREV_PAGE]: '_prevPage',
          [this.Shortcut.OPEN_CHANGE]: '_openChange',
          [this.Shortcut.TOGGLE_CHANGE_REVIEWED]: '_toggleChangeReviewed',
          [this.Shortcut.TOGGLE_CHANGE_STAR]: '_toggleChangeStar',
          [this.Shortcut.REFRESH_CHANGE_LIST]: '_refreshChangeList',
        };
      },
  
      attached() {
        Gerrit.awaitPluginsLoaded().then(() => {
          this._dynamicHeaderEndpoints = Gerrit._endpoints.getDynamicEndpoints(
              'change-list-header');
        });
      },
  
      /**
       * Iron-a11y-keys-behavior catches keyboard events globally. Some keyboard
       * events must be scoped to a component level (e.g. `enter`) in order to not
       * override native browser functionality.
       *
       * Context: Issue 7294
       */
      _scopedKeydownHandler(e) {
        if (e.keyCode === 13) {
          // Enter.
          this._openChange(e);
        }
      },
  
      _lowerCase(column) {
        return column.toLowerCase();
      },
  
      _computePreferences(account, preferences) {
        // Polymer 2: check for undefined
        if ([account, preferences].some(arg => arg === undefined)) {
          return;
        }
  
        this.changeTableColumns = this.columnNames;
  
        if (account) {
          this.showNumber = !!(preferences &&
              preferences.legacycid_in_change_table);
          this.visibleChangeTableColumns = preferences.change_table.length > 0 ?
              this.getVisibleColumns(preferences.change_table) : this.columnNames;
        } else {
          // Not logged in.
          this.showNumber = false;
          this.visibleChangeTableColumns = this.columnNames;
        }
      },
  
      _computeColspan(changeTableColumns, labelNames) {
        if (!changeTableColumns || !labelNames) return;
        return changeTableColumns.length + labelNames.length +
            NUMBER_FIXED_COLUMNS;
      },
  
      _computeLabelNames(sections) {
        if (!sections) { return []; }
        let labels = [];
        const nonExistingLabel = function(item) {
          return !labels.includes(item);
        };
        for (const section of sections) {
          if (!section.results) { continue; }
          for (const change of section.results) {
            if (!change.labels) { continue; }
            const currentLabels = Object.keys(change.labels);
            labels = labels.concat(currentLabels.filter(nonExistingLabel));
          }
        }
        return labels.sort();
      },
  
      _computeLabelShortcut(labelName) {
        if (labelName.startsWith(LABEL_PREFIX_INVALID_PROLOG)) {
          labelName = labelName.slice(LABEL_PREFIX_INVALID_PROLOG.length);
        }
        return labelName.split('-')
            .reduce((a, i) => {
              if (!i) { return a; }
              return a + i[0].toUpperCase();
            }, '')
            .slice(0, MAX_SHORTCUT_CHARS);
      },
  
      _changesChanged(changes) {
        this.sections = changes ? [{results: changes}] : [];
      },
  
      _processQuery(query) {
        let tokens = query.split(' ');
        const invalidTokens = ['limit:', 'age:', '-age:'];
        tokens = tokens.filter(token => {
          return !invalidTokens.some(invalidToken => {
            return token.startsWith(invalidToken);
          });
        });
        return tokens.join(' ');
      },
  
      _sectionHref(query) {
        return Gerrit.Nav.getUrlForSearchQuery(this._processQuery(query));
      },
  
      /**
       * Maps an index local to a particular section to the absolute index
       * across all the changes on the page.
       *
       * @param {number} sectionIndex index of section
       * @param {number} localIndex index of row within section
       * @return {number} absolute index of row in the aggregate dashboard
       */
      _computeItemAbsoluteIndex(sectionIndex, localIndex) {
        let idx = 0;
        for (let i = 0; i < sectionIndex; i++) {
          idx += this.sections[i].results.length;
        }
        return idx + localIndex;
      },
  
      _computeItemSelected(sectionIndex, index, selectedIndex) {
        const idx = this._computeItemAbsoluteIndex(sectionIndex, index);
        return idx == selectedIndex;
      },
  
      _computeItemNeedsReview(account, change, showReviewedState) {
        return showReviewedState && !change.reviewed &&
            !change.work_in_progress &&
            this.changeIsOpen(change) &&
            (!account || account._account_id != change.owner._account_id);
      },
  
      _computeItemHighlight(account, change) {
        // Do not show the assignee highlight if the change is not open.
        if (!change ||!change.assignee ||
            !account ||
            CLOSED_STATUS.indexOf(change.status) !== -1) {
          return false;
        }
        return account._account_id === change.assignee._account_id;
      },
  
      _nextChange(e) {
        if (this.shouldSuppressKeyboardShortcut(e) ||
            this.modifierPressed(e)) { return; }
  
        e.preventDefault();
        this.$.cursor.next();
      },
  
      _prevChange(e) {
        if (this.shouldSuppressKeyboardShortcut(e) ||
            this.modifierPressed(e)) { return; }
  
        e.preventDefault();
        this.$.cursor.previous();
      },
  
      _openChange(e) {
        if (this.shouldSuppressKeyboardShortcut(e) ||
            this.modifierPressed(e)) { return; }
  
        e.preventDefault();
        Gerrit.Nav.navigateToChange(this._changeForIndex(this.selectedIndex));
      },
  
      _nextPage(e) {
        if (this.shouldSuppressKeyboardShortcut(e) ||
            this.modifierPressed(e) && !this.isModifierPressed(e, 'shiftKey')) {
          return;
        }
  
        e.preventDefault();
        this.fire('next-page');
      },
  
      _prevPage(e) {
        if (this.shouldSuppressKeyboardShortcut(e) ||
            this.modifierPressed(e) && !this.isModifierPressed(e, 'shiftKey')) {
          return;
        }
  
        e.preventDefault();
        this.fire('previous-page');
      },
  
      _toggleChangeReviewed(e) {
        if (this.shouldSuppressKeyboardShortcut(e) ||
            this.modifierPressed(e)) { return; }
  
        e.preventDefault();
        this._toggleReviewedForIndex(this.selectedIndex);
      },
  
      _toggleReviewedForIndex(index) {
        const changeEls = this._getListItems();
        if (index >= changeEls.length || !changeEls[index]) {
          return;
        }
  
        const changeEl = changeEls[index];
        changeEl.toggleReviewed();
      },
  
      _refreshChangeList(e) {
        if (this.shouldSuppressKeyboardShortcut(e)) { return; }
  
        e.preventDefault();
        this._reloadWindow();
      },
  
      _reloadWindow() {
        window.location.reload();
      },
  
      _toggleChangeStar(e) {
        if (this.shouldSuppressKeyboardShortcut(e) ||
            this.modifierPressed(e)) { return; }
  
        e.preventDefault();
        this._toggleStarForIndex(this.selectedIndex);
      },
  
      _toggleStarForIndex(index) {
        const changeEls = this._getListItems();
        if (index >= changeEls.length || !changeEls[index]) {
          return;
        }
  
        const changeEl = changeEls[index];
        changeEl.$$('gr-change-star').toggleStar();
      },
  
      _changeForIndex(index) {
        const changeEls = this._getListItems();
        if (index < changeEls.length && changeEls[index]) {
          return changeEls[index].change;
        }
        return null;
      },
  
      _getListItems() {
        return Array.from(
            Polymer.dom(this.root).querySelectorAll('gr-change-list-item'));
      },
  
      _sectionsChanged() {
        // Flush DOM operations so that the list item elements will be loaded.
        Polymer.RenderStatus.afterNextRender(this, () => {
          this.$.cursor.stops = this._getListItems();
          this.$.cursor.moveToStart();
        });
      },
  
      _isOutgoing(section) {
        return !!section.isOutgoing;
      },
  
      _isEmpty(section) {
        return !section.results.length;
      },
    });
  })();
  