"use strict";

/**
 * Site Header
 */
RB.HeaderView = Backbone.View.extend({
  SEARCH_SUMMARY_TRIM_LEN: 28,
  DESKTOP_SEARCH_RESULTS_WIDTH: 350,
  events: {
    'click #nav_toggle': '_handleNavClick',
    'touchstart #nav_toggle': '_handleNavClick',
    'focus #search_field': '_closeMobileMenu'
  },
  /**
   * Ensure that this HeaderView is the only one on the page.
   *
   * This exists in a separate function so that unit tests can disable this
   * check.
   */
  _ensureSingleton() {
    if (RB.HeaderView.instance !== null) {
      console.warn('There are two instances of RB.HeaderView on the ' + 'page. Make sure only one RB.PageView is ' + 'instantiated and registered through ' + 'RB.PageManager.setupPage().');
    } else {
      RB.HeaderView.instance = this;
    }
  },
  /**
   * Initialize the header.
   *
   * Args:
   *     options (object, optional):
   *         Options for the view.
   *
   * Option Args:
   *     $body (jQuery, optional):
   *         The body element. This is useful for unit tests.
   *
   *     $pageSidebar (jQuery, optional):
   *         The page sidebar element. This is useful for unit tests.
   */
  initialize(options = {}) {
    this._ensureSingleton();
    this.options = options;

    /*
     * This is used by RB.PageManager to determine if a RB.PageView
     * subclass has correctly rendered a HeaderView, or if PageManager
     * needs to take care of it.
     *
     * This is deprecated and can be removed in Review Board 5.0.
     */
    this.isRendered = false;
    this._mobileMenuOpened = false;
    this._$window = null;
    this._$body = null;
    this._$navToggle = null;
    this._$mobileMenuMask = null;
  },
  /**
   * Remove the view from the DOM and disable event handling.
   *
   * This will also unset :js:attr:`RB.HeaderView.instance`, if currently
   * set to this instance.
   */
  remove() {
    if (RB.HeaderView.instance === this) {
      RB.HeaderView.instance = null;
    }
    if (this._$window) {
      this._$window.off('resize.rbHeaderView', this._recalcFunc);
    }
    Backbone.View.prototype.remove.call(this);
  },
  /**
   * Render the header.
   *
   * Returns:
   *     RB.HeaderView:
   *     This view, for chaining.
   */
  render() {
    const options = this.options;
    this._$window = $(window);
    this._$body = options.body || $(document.body);
    this._$navToggle = $('#nav_toggle');
    this._$mobileMenuMask = $('<div id="mobile_menu_mask">').on('click touchstart', this._closeMobileMenu.bind(this)).insertAfter(options.$pageSidebar || $('#page-sidebar'));
    this._recalcFunc = _.throttle(this._recalcMobileMode.bind(this), 100);
    this._$window.on('resize.rbHeaderView', this._recalcFunc);
    this._recalcMobileMode();
    this._setupSearch();
    this.isRendered = true;
    return this;
  },
  /**
   * Handle a click on the mobile navigation menu.
   *
   * Args:
   *     e (Event):
   *         The click event.
   */
  _handleNavClick(e) {
    e.stopPropagation();
    e.preventDefault();
    this._setMobileMenuOpened(!this._mobileMenuOpened);
  },
  /**
   * Recalculate whether the header and nav menu is in mobile mode.
   */
  _recalcMobileMode() {
    const inMobileMode = this._$navToggle.is(':visible');
    if (inMobileMode === this.inMobileMode) {
      return;
    }
    if (!inMobileMode) {
      this._setMobileMenuOpened(false);
    }
    this.inMobileMode = inMobileMode;
    this.trigger('mobileModeChanged', inMobileMode);
  },
  /**
   * Close the mobile menu.
   *
   * This is used as an event handler, to make it easy to close the
   * mobile menu and prevent any bubbling or default actions from the
   * event.
   */
  _closeMobileMenu() {
    this._setMobileMenuOpened(false);
    return false;
  },
  /**
   * Set up the search auto-complete field.
   */
  _setupSearch() {
    this._$search = $('#search_field').rbautocomplete({
      formatItem: data => {
        let s;
        if (data.username) {
          // For the format of users:
          s = data.username;
          if (data.fullname) {
            s += ` <span>(${_.escape(data.fullname)})</span>`;
          }
        } else if (data.name) {
          // For the format of groups:
          const displayName = _.escape(data.display_name);
          s = `${data.name} <span>(${displayName})</span>`;
        } else if (data.summary) {
          // For the format of review requests:
          if (data.summary.length < this.SEARCH_SUMMARY_TRIM_LEN) {
            s = data.summary;
          } else {
            s = data.summary.substring(0, this.SEARCH_SUMMARY_TRIM_LEN);
          }
          s += ` <span>(${_.escape(data.id)})</span>`;
        }
        return s;
      },
      matchCase: false,
      multiple: false,
      clickToURL: true,
      selectFirst: false,
      width: this.DESKTOP_SEARCH_RESULTS_WIDTH,
      enterToURL: true,
      resultsParentEl: $('#page-container'),
      resultsClass: 'ui-autocomplete-results search-results',
      parse: data => {
        const objects = ['users', 'groups', 'review_requests'];
        const keys = ['username', 'name', 'summary'];
        const parsed = [];
        for (let j = 0; j < objects.length; j++) {
          const object = objects[j];
          const items = data.search[object];
          for (let i = 0; i < items.length; i++) {
            const value = items[i];
            const key = keys[j];
            if (j !== 2) {
              // For users and groups, always show.
              parsed.push({
                data: value,
                value: value[key],
                result: value[key]
              });
            } else if (value.public) {
              /*
               * For review requests, only show ones that are
               * public.
               */
              parsed.push({
                data: value,
                value: value[key],
                result: value[key]
              });
            }
          }
        }
        return parsed;
      },
      url: `${SITE_ROOT}api/search/`,
      extraParams: {
        'only-fields': 'display_name,fullname,id,name,public,summary,url,username',
        'only-links': ''
      }
    });
  },
  /**
   * Set whether the mobile menu is opened.
   *
   * Args:
   *     opened (boolean):
   *         Whether the menu is open.
   */
  _setMobileMenuOpened(opened) {
    if (opened === this._mobileMenuOpened) {
      return;
    }
    if (opened) {
      this._$mobileMenuMask.show();
      _.defer(() => this._$body.addClass('js-mobile-menu-open'));
    } else {
      this._$body.removeClass('js-mobile-menu-open');
      _.delay(() => this._$mobileMenuMask.hide(), 300);
    }
    this._mobileMenuOpened = opened;
  }
}, {
  /** The instance of the HeaderView. */
  instance: null
});

//# sourceMappingURL=headerView.js.map