| define(
  'tinymce.themes.mobile.ui.StylesMenu',
  [
    'ephox.alloy.api.behaviour.AddEventsBehaviour',
    'ephox.alloy.api.behaviour.Behaviour',
    'ephox.alloy.api.behaviour.Representing',
    'ephox.alloy.api.behaviour.Toggling',
    'ephox.alloy.api.behaviour.Transitioning',
    'ephox.alloy.api.component.GuiFactory',
    'ephox.alloy.api.component.Memento',
    'ephox.alloy.api.events.AlloyEvents',
    'ephox.alloy.api.ui.Button',
    'ephox.alloy.api.ui.Menu',
    'ephox.alloy.api.ui.TieredMenu',
    'ephox.boulder.api.Objects',
    'ephox.katamari.api.Arr',
    'ephox.katamari.api.Merger',
    'ephox.katamari.api.Obj',
    'ephox.sugar.api.properties.Css',
    'ephox.sugar.api.search.SelectorFind',
    'ephox.sugar.api.view.Width',
    'tinymce.themes.mobile.channels.Receivers',
    'tinymce.themes.mobile.style.Styles',
    'tinymce.themes.mobile.touch.scroll.Scrollable'
  ],
  function (
    AddEventsBehaviour, Behaviour, Representing, Toggling, Transitioning, GuiFactory, Memento,
    AlloyEvents, Button, Menu, TieredMenu, Objects, Arr, Merger, Obj, Css, SelectorFind,
    Width, Receivers, Styles, Scrollable
  ) {
    var getValue = function (item) {
      return Objects.readOptFrom(item, 'format').getOr(item.title);
    };
    var convert = function (formats, memMenuThunk) {
      var mainMenu = makeMenu('Styles', [
      ].concat(
        Arr.map(formats.items, function (k) {
          return makeItem(getValue(k), k.title, k.isSelected(), k.getPreview(), Objects.hasKey(formats.expansions, getValue(k)));
        })
      ), memMenuThunk, false);
      var submenus = Obj.map(formats.menus, function (menuItems, menuName) {
        var items = Arr.map(menuItems, function (item) {
          return makeItem(
            getValue(item),
            item.title,
            item.isSelected !== undefined ? item.isSelected() : false,
            item.getPreview !== undefined ? item.getPreview() : '',
            Objects.hasKey(formats.expansions, getValue(item))
          );
        });
        return makeMenu(menuName, items, memMenuThunk, true);
      });
      var menus = Merger.deepMerge(submenus, Objects.wrap('styles', mainMenu));
      var tmenu = TieredMenu.tieredData('styles', menus, formats.expansions);
      return {
        tmenu: tmenu
      };
    };
    var makeItem = function (value, text, selected, preview, isMenu) {
      return {
        data: {
          value: value,
          text: text
        },
        type: 'item',
        dom: {
          tag: 'div',
          classes: isMenu ? [ Styles.resolve('styles-item-is-menu') ] : [ ]
        },
        toggling: {
          toggleOnExecute: false,
          toggleClass: Styles.resolve('format-matches'),
          selected: selected
        },
        itemBehaviours: Behaviour.derive(isMenu ? [ ] : [
          Receivers.format(value, function (comp, status) {
            var toggle = status ? Toggling.on : Toggling.off;
            toggle(comp);
          })
        ]),
        components: [
          {
            dom: {
              tag: 'div',
              attributes: {
                style: preview
              },
              innerHtml: text
            }
          }
        ]
      };
    };
    var makeMenu = function (value, items, memMenuThunk, collapsable) {
      return {
        value: value,
        dom: {
          tag: 'div'
        },
        components: [
          Button.sketch({
            dom: {
              tag: 'div',
              classes: [ Styles.resolve('styles-collapser') ]
            },
            components: collapsable ? [
              {
                dom: {
                  tag: 'span',
                  classes: [ Styles.resolve('styles-collapse-icon') ]
                }
              },
              GuiFactory.text(value)
            ] : [ GuiFactory.text(value) ],
            action: function (item) {
              if (collapsable) {
                var comp = memMenuThunk().get(item);
                TieredMenu.collapseMenu(comp);
              }
            }
          }),
          {
            dom: {
              tag: 'div',
              classes: [ Styles.resolve('styles-menu-items-container') ]
            },
            components: [
              Menu.parts().items({ })
            ],
            behaviours: Behaviour.derive([
              AddEventsBehaviour.config('adhoc-scrollable-menu', [
                AlloyEvents.runOnAttached(function (component, simulatedEvent) {
                  Css.set(component.element(), 'overflow-y', 'auto');
                  Css.set(component.element(), '-webkit-overflow-scrolling', 'touch');
                  Scrollable.register(component.element());
                }),
                AlloyEvents.runOnDetached(function (component) {
                  Css.remove(component.element(), 'overflow-y');
                  Css.remove(component.element(), '-webkit-overflow-scrolling');
                  Scrollable.deregister(component.element());
                })
              ])
            ])
          }
        ],
        items: items,
        menuBehaviours: Behaviour.derive([
          Transitioning.config({
            initialState: 'after',
            routes: Transitioning.createTristate('before', 'current', 'after', {
              transition: {
                property: 'transform',
                transitionClass: 'transitioning'
              }
            })
          })
        ])
      };
    };
    var sketch = function (settings) {
      var dataset = convert(settings.formats, function () {
        return memMenu;
      });
      // Turn settings into a tiered menu data.
      var memMenu = Memento.record(TieredMenu.sketch({
        dom: {
          tag: 'div',
          classes: [ Styles.resolve('styles-menu') ]
        },
        components: [ ],
        // Focus causes issues when the things being focused are offscreen.
        fakeFocus: true,
        // For animations, need things to stay around in the DOM (at least until animation is done)
        stayInDom: true,
        onExecute: function (tmenu, item) {
          var v = Representing.getValue(item);
          settings.handle(item, v.value);
        },
        onEscape: function () {
        },
        onOpenMenu: function (container, menu) {
          var w = Width.get(container.element());
          Width.set(menu.element(), w);
          Transitioning.jumpTo(menu, 'current');
        },
        onOpenSubmenu: function (container, item, submenu) {
          var w = Width.get(container.element());
          var menu = SelectorFind.ancestor(item.element(), '[role="menu"]').getOrDie('hacky');
          var menuComp = container.getSystem().getByDom(menu).getOrDie();
          Width.set(submenu.element(), w);
          Transitioning.progressTo(menuComp, 'before');
          Transitioning.jumpTo(submenu, 'after');
          Transitioning.progressTo(submenu, 'current');
        },
        onCollapseMenu: function (container, item, menu) {
          var submenu = SelectorFind.ancestor(item.element(), '[role="menu"]').getOrDie('hacky');
          var submenuComp = container.getSystem().getByDom(submenu).getOrDie();
          Transitioning.progressTo(submenuComp, 'after');
          Transitioning.progressTo(menu, 'current');
        },
        navigateOnHover: false,
        openImmediately: true,
        data: dataset.tmenu,
        markers: {
          backgroundMenu: Styles.resolve('styles-background-menu'),
          menu: Styles.resolve('styles-menu'),
          selectedMenu: Styles.resolve('styles-selected-menu'),
          item: Styles.resolve('styles-item'),
          selectedItem: Styles.resolve('styles-selected-item')
        }
      }));
      return memMenu.asSpec();
    };
    return {
      sketch: sketch
    };
  }
);
 |