d;
    if (this._closedClients[id]) {
      this._selectParentRow();
    }
    this._change("all");
  },

  _isOpen(client) {
    return !this._closedClients[client.id];
  },

  moveSelectionDown() {
    let branchRow = this._selectedRow[0];
    let childRow = this._selectedRow[1];
    let branch = this.data[branchRow];

    if (this.filter) {
      this.selectRow(branchRow + 1);
      return;
    }

    if (branchRow < 0) {
      this.selectRow(0, -1);
    } else if (
      (!branch.tabs.length ||
        childRow >= branch.tabs.length - 1 ||
        !this._isOpen(branch)) &&
      branchRow < this.data.length
    ) {
      this.selectRow(branchRow + 1, -1);
    } else if (childRow < branch.tabs.length) {
      this.selectRow(branchRow, childRow + 1);
    }
  },

  moveSelectionUp() {
    let branchRow = this._selectedRow[0];
    let childRow = this._selectedRow[1];

    if (this.filter) {
      this.selectRow(branchRow - 1);
      return;
    }

    if (branchRow < 0) {
      this.selectRow(0, -1);
    } else if (childRow < 0 && branchRow > 0) {
      let prevBranch = this.data[branchRow - 1];
      let newChildRow = this._isOpen(prevBranch)
        ? prevBranch.tabs.length - 1
        : -1;
      this.selectRow(branchRow - 1, newChildRow);
    } else if (childRow >= 0) {
      this.selectRow(branchRow, childRow - 1);
    }
  },

  // Selects a row and makes sure the selection is within bounds
  selectRow(parent, child) {
    let maxParentRow = this.filter ? this._tabCount() : this.data.length;
    let parentRow = parent;
    if (parent <= -1) {
      parentRow = 0;
    } else if (parent >= maxParentRow) {
      return;
    }

    let childRow = child;
    if (
      parentRow === -1 ||
      this.filter ||
      typeof child === "undefined" ||
      child < -1
    ) {
      childRow = -1;
    } else if (child >= this.data[parentRow].tabs.length) {
      childRow = this.data[parentRow].tabs.length - 1;
    }

    if (
      this._selectedRow[0] === parentRow &&
      this._selectedRow[1] === childRow
    ) {
      return;
    }

    this._selectedRow = [parentRow, childRow];
    this.inputFocused = false;
    this._change("all");
    // Record the telemetry event
    let extraOptions = {
      tab_pos: this._selectedRow[1].toString(),
      filter: this.filter,
    };
    this._SyncedTabs.recordSyncedTabsTelemetry(
      "synced_tabs_sidebar",
      "click",
      extraOptions
    );
  },

  _tabCount() {
    return this.data.reduce((prev, curr) => curr.tabs.length + prev, 0);
  },

  toggleBranch(id) {
    this._toggleBranch(id, !this._closedClients[id]);
  },

  closeBranch(id) {
    this._toggleBranch(id, true);
  },

  openBranch(id) {
    this._toggleBranch(id, false);
  },

  focusInput() {
    this.inputFocused = true;
    // A change type of "all" updates rather than rebuilds, which is what we
    // want here - only the selection/focus has changed.
    this._change("all");
  },

  blurInput() {
    this.inputFocused = false;
    // A change type of "all" updates rather than rebuilds, which is what we
    // want here - only the selection/focus has changed.
    this._change("all");
  },

  clearFilter() {
    this.filter = "";
    this._selectedRow = [-1, -1];
    return this.getData();
  },

  // Fetches data from the SyncedTabs module and triggers
  // and update
  getData(filter) {
    let updateType;
    let hasFilter = typeof filter !== "undefined";
    if (hasFilter) {
      this.filter = filter;
      this._selectedRow = [-1, -1];

      // When a filter is specified we tell the view that only the list
      // needs to be rerendered so that it doesn't disrupt the input
      // field's focus.
      updateType = "searchbox";
    }

    // return promise for tests
    return this._SyncedTabs
      .getTabClients(this.filter)
      .then(result => {
        if (!hasFilter) {
          // Only sort clients and tabs if we're rendering the whole list.
          this._SyncedTabs.sortTabClientsByLastUsed(result);
        }
        this.data = result;
        this._change(updateType);
      })
      .catch(console.error);
  },
});
PK