Source: js/ime_menu.js

'use strict';

/* global MozActivity, Tagged */
(function(exports) {
  /**
   * ImeMenu displays a list of currently enabled IMEs in an overlay.
   * @class ImeMenu
   * @param {Array} listItems An array of objects to display.
   * @param {String} title L10n ID of the the content of the header.
   * @param {Function} successCb Called when the user selects an option.
   * @param {Function} cancelCb Called when the menu is cancelled.
   */
  function ImeMenu(listItems, title, successCb, cancelCb) {
    this.onselected = successCb || function() {};
    this.oncancel = cancelCb || function() {};
    this.listItems = listItems;
    this.title = title;
  }

  ImeMenu.prototype = {
    /**
     * Start the ImeMenu instance
     * @memberof ImeMenu.prototype
     */
    start: function() {
      this.initUI();
    },

    /**
     * Builds dom and adds event listeners
     * @memberof ImeMenu.prototype
     */
    initUI: function() {
      var dummy = document.createElement('div');

      dummy.innerHTML = this.imeMenuView({
        title: this.title
      });
      this.container = dummy.firstElementChild;

      // We have a menu with all the options
      this.menu = this.container.querySelector('.ime-menu-list');
      this.buildMenu(this.listItems);

      // We append to System app (actually to '#screen')
      document.getElementById('screen').appendChild(this.container);

      this.container.addEventListener('submit', this);
      this.container.addEventListener('click', this);

      window.addEventListener('attentionopened', this, true);
      window.addEventListener('screenchange', this, true);
      window.addEventListener('home', this);
      window.addEventListener('holdhome', this);

      this.container.addEventListener('mousedown', this.preventFocusChange);

      window.dispatchEvent(new CustomEvent('imemenushow'));
    },

    /**
     * Removes the dom and stops event listeners
     * @memberof ImeMenu.prototype
     */
    stop: function() {
      document.getElementById('screen').removeChild(this.container);

      window.removeEventListener('attentionopened', this, true);
      window.removeEventListener('screenchange', this, true);
      window.removeEventListener('home', this);
      window.removeEventListener('holdhome', this);

      this.container.removeEventListener('mousedown', this.preventFocusChange);
    },

    /**
     * Returns the view for the ime menu.
     * @memberof ImeMenu.prototype
     */
    imeMenuView: function({title, cancelLabel}) {
      return Tagged.escapeHTML `<form role="dialog" data-type="value-selector"
        class="ime-menu value-selector-container"
        data-z-index-level="action-menu">
        <section>
          <h1 data-l10n-id="${title}"></h1>
          <ol class="value-selector-options-container ime-menu-list"
            aria-multiselectable="false" role="listbox">
          </ol>
        </section>
        <menu class="ime-menu-button-container">
          <button class="ime-menu-button" data-type="cancel"
            data-action="cancel" data-l10n-id="cancel"></button>
        </menu>
      </div>`;
    },

    /**
     * Returns the view for a menu item.
     * @memberof ImeMenu.prototype
     */
    menuItemView: function({layoutName, appName, layoutId, selected}) {
      return Tagged.escapeHTML `<li role="option" aria-selected="${selected}"
        data-id="${layoutId}">
        <label role="presentation">
          <span class="item-label">${layoutName}</span>
          <span class="item-note">${appName}</span>
        </label>
      </li>`;
    },

    /**
     * Builds the dom for the menu.
     * @memberof ImeMenu.prototype
     */
    buildMenu: function(items) {
      this.menu.innerHTML = '';
      items.forEach(function traveseItems(item) {
        this.menu.innerHTML += this.menuItemView({
          layoutName: item.layoutName,
          appName: item.appName,
          layoutId: item.value.toString(),
          selected: item.selected ? 'true' : 'false'
        });
      }, this);
    },

    /**
     * Hides the ImeMenu.
     * @memberof ImeMenu.prototype
     * @param  {Function} callback The callback to call after hiding.
     */
    hide: function(callback) {
      this.stop();
      if (callback && typeof callback === 'function') {
        setTimeout(callback);
      }
    },

    /**
     * When IME switcher shows, prevent the keyboard focus getting changed.
     * @memberof ImeMenu.prototype
     * @param  {DOMEvent} evt The event.
     */
    preventFocusChange: function(evt) {
       evt.preventDefault();
    },

    /**
     * General event handler interface.
     * Handles submission and cancellation events.
     * @memberof ImeMenu.prototype
     * @param  {DOMEvent} evt The event.
     */
    handleEvent: function(evt) {
      var target = evt.target;
      var type = evt.type;
      switch (type) {
        case 'submit':
          evt.preventDefault();
          break;
        case 'screenchange':
          if (!evt.detail.screenEnabled) {
            this.hide();
            this.oncancel();
          }
          break;

        case 'click':
          evt.preventDefault();
          var action = target.dataset.action;
          if (action) {
            if (action === 'cancel') {
              this.hide();
              this.oncancel();
              return;
            }

            if (action === 'settings') {
              this.hide();
              this.launchSettings();
              return;
            }
          }

          var id = target.dataset.id;
          if (!id) {
            return;
          }
          id = parseInt(id);
          this.hide(this.onselected.bind(this, id));
          break;

        case 'home':
        case 'holdhome':
          this.hide();
          this.oncancel();
          break;

        case 'attentionopened':
          this.hide();
          break;
      }
    },

    /**
     * To lauch the settings via web activity.
     * @memberof ImeMenu.prototype
     */
    launchSettings: function() {
      var activity = new MozActivity({
        name: 'configure',
        data: {
          target: 'device',
          section: 'keyboard'
        }
      });
      activity.onerror = function() {
        console.error('Failed to invoke keyboard settings.');
      };
    }
  };

  exports.ImeMenu = ImeMenu;
}(window));