(function (root, factory) {
  const exported = factory();
  if (typeof module === 'object' && module.exports) module.exports = exported;
  else root.createMobileComposerViewport = exported;
})(typeof self !== 'undefined' ? self : this, function () {
  return function createMobileComposerViewport(options) {
    const entries = options.entries || [];
    const viewport = options.viewport;
    const mediaQuery = options.mediaQuery;
    const documentRef = options.document || document;
    const schedule = options.schedule || (callback => requestAnimationFrame(callback));
    let active = null;
    let started = false;

    function reveal() {
      if (!active) return;
      const focused = documentRef.activeElement;
      const target = active.focusWithin && active.workspace.contains(focused) ? focused : active.workspace;
      target.scrollIntoView({ block:active.focusWithin ? 'nearest' : 'end', inline:'nearest' });
    }

    function applyGeometry() {
      if (!active || !viewport || !mediaQuery.matches) return;
      active.panel.style.setProperty('--composer-viewport-top', `${viewport.offsetTop || 0}px`);
      active.panel.style.setProperty('--composer-viewport-height', `${viewport.height}px`);
      reveal();
    }

    function onViewportChange() {
      schedule(applyGeometry);
    }

    function activate(entry) {
      if (!viewport || !mediaQuery.matches) return;
      if (active === entry) {
        schedule(applyGeometry);
        return;
      }
      if (active) deactivate(active, false);
      active = entry;
      entry.scrollTop = entry.panel.scrollTop;
      entry.panel.classList?.add('composer-keyboard-active');
      viewport.addEventListener('resize', onViewportChange);
      viewport.addEventListener('scroll', onViewportChange);
      applyGeometry();
    }

    function deactivate(entry, restore = true) {
      if (active !== entry) return;
      viewport?.removeEventListener('resize', onViewportChange);
      viewport?.removeEventListener('scroll', onViewportChange);
      entry.panel.classList?.remove('composer-keyboard-active');
      entry.panel.style.removeProperty('--composer-viewport-top');
      entry.panel.style.removeProperty('--composer-viewport-height');
      active = null;
      if (restore) schedule(() => { entry.panel.scrollTop = entry.scrollTop; });
    }

    function bind(entry) {
      entry.onFocus = () => activate(entry);
      entry.onFocusOut = () => schedule(() => {
        if (!entry.workspace.contains(documentRef.activeElement)) deactivate(entry);
      });
      entry.focusTarget = entry.focusWithin ? entry.workspace : entry.composer;
      entry.focusEvent = entry.focusWithin ? 'focusin' : 'focus';
      entry.focusTarget.addEventListener(entry.focusEvent, entry.onFocus);
      entry.workspace.addEventListener('focusout', entry.onFocusOut);
    }

    function unbind(entry) {
      entry.focusTarget.removeEventListener(entry.focusEvent, entry.onFocus);
      entry.workspace.removeEventListener('focusout', entry.onFocusOut);
    }

    return {
      start() {
        if (started) return;
        started = true;
        entries.forEach(bind);
      },
      close(panel) {
        const entry = entries.find(candidate => candidate.panel === panel);
        if (entry) deactivate(entry, false);
      },
      stop() {
        if (!started) return;
        if (active) deactivate(active, false);
        entries.forEach(unbind);
        started = false;
      },
      revealActive() {
        schedule(applyGeometry);
      },
    };
  };
});
