text: /**
   * Copyright IBM Corp. 2016, 2018
   *
   * This source code is licensed under the Apache-2.0 license found in the
   * LICENSE file in the root directory of this source tree.
   */
  
  export default function(ToMix) {
    /**
     * Mix-in class to manage events associated with states.
     * @class EventedState
     */
    class EventedState extends ToMix {
      /* eslint-disable jsdoc/check-param-names */
      /**
       * The internal implementation for {@link EventedState#changeState `.changeState()`}, performing actual change in state.
       * @param {string} [state] The new state. Can be an omitted, which means toggling.
       * @param {object} [detail]
       *   The object that should be put to event details that is fired before/after changing state.
       *   Can have a `group` property, which specifies what state to be changed.
       * @param {EventedState~changeStateCallback} callback The callback called once changing state is finished or is canceled.
       * @private
       */
      _changeState() {
        throw new Error(
          '_changeState() should be overriden to perform actual change in state.'
        );
      }
  
      /**
       * Changes the state of this component.
       * @param {string} [state] The new state. Can be an omitted, which means toggling.
       * @param {object} [detail]
       *   The object that should be put to event details that is fired before/after changing state.
       *   Can have a `group` property, which specifies what state to be changed.
       * @param {EventedState~changeStateCallback} [callback] The callback called once changing state is finished or is canceled.
       */
      changeState(...args) {
        const state = typeof args[0] === 'string' ? args.shift() : undefined;
        const detail =
          Object(args[0]) === args[0] && typeof args[0] !== 'function'
            ? args.shift()
            : undefined;
        const callback = typeof args[0] === 'function' ? args.shift() : undefined;
  
        if (
          typeof this.shouldStateBeChanged === 'function' &&
          !this.shouldStateBeChanged(state, detail)
        ) {
          if (callback) {
            callback(null, true);
          }
          return;
        }
  
        const data = {
          group: detail && detail.group,
          state,
        };
  
        const eventNameSuffix = [data.group, state]
          .filter(Boolean)
          .join('-')
          .split('-') // Group or state may contain hyphen
          .map(item => item[0].toUpperCase() + item.substr(1))
          .join('');
  
        const eventStart = new CustomEvent(
          this.options[`eventBefore${eventNameSuffix}`],
          {
            bubbles: true,
            cancelable: true,
            detail,
          }
        );
  
        const fireOnNode = (detail && detail.delegatorNode) || this.element;
        const canceled = !fireOnNode.dispatchEvent(eventStart);
  
        if (canceled) {
          if (callback) {
            const error = new Error(
              `Changing state (${JSON.stringify(data)}) has been canceled.`
            );
            error.canceled = true;
            callback(error);
          }
        } else {
          const changeStateArgs = [state, detail].filter(Boolean);
          this._changeState(...changeStateArgs, () => {
            fireOnNode.dispatchEvent(
              new CustomEvent(this.options[`eventAfter${eventNameSuffix}`], {
                bubbles: true,
                cancelable: true,
                detail,
              })
            );
            if (callback) {
              callback();
            }
          });
        }
      }
      /* eslint-enable jsdoc/check-param-names */
  
      /**
       * Tests if change in state should happen or not.
       * Classes inheriting {@link EventedState `EventedState`} should override this function.
       * @function EventedState#shouldStateBeChanged
       * @param {string} [state] The new state. Can be an omitted, which means toggling.
       * @param {object} [detail]
       *   The object that should be put to event details that is fired before/after changing state.
       *   Can have a `group` property, which specifies what state to be changed.
       * @returns {boolean}
       *   `false` if change in state shouldn't happen, e.g. when the given new state is the same as the current one.
       */
    }
  
    /**
     * The callback called once changing state is finished or is canceled.
     * @callback EventedState~changeStateCallback
     * @param {Error} error
     *   An error object with `true` in its `canceled` property if changing state is canceled.
     *   Cancellation happens if the handler of a custom event, that is fired before changing state happens,
     *   calls `.preventDefault()` against the event.
     * @param {boolean} keptState
     *   `true` if the call to {@link EventedState#changeState `.changeState()`} didn't cause actual change in state.
     */
  
    return EventedState;
  }
  