| define(
  'tinymce.themes.mobile.android.core.AndroidEvents',
  [
    'ephox.alloy.api.behaviour.Toggling',
    'ephox.katamari.api.Arr',
    'ephox.katamari.api.Fun',
    'ephox.sand.api.PlatformDetection',
    'ephox.sugar.api.dom.Compare',
    'ephox.sugar.api.dom.Focus',
    'ephox.sugar.api.events.DomEvent',
    'ephox.sugar.api.node.Element',
    'ephox.sugar.api.node.Node',
    'ephox.sugar.api.search.Traverse',
    'tinymce.themes.mobile.util.TappingEvent'
  ],
  function (Toggling, Arr, Fun, PlatformDetection, Compare, Focus, DomEvent, Element, Node, Traverse, TappingEvent) {
    var isAndroid6 = PlatformDetection.detect().os.version.major >= 6;
    /*
      `selectionchange` on the iframe document. If the selection is *ranged*, then we add the margin, because we
      assume that the context menu has appeared. If it is collapsed, then the context menu shouldn't appear
      (there is no selected text to format), so we reset the margin to `0px`. Note, when adding a margin,
      we add `23px` --- this is most likely based on trial and error. We may need to work out how to get
      this value properly.
      2. `select` on the outer document. This will also need to add the margin if the selection is ranged within
      an input or textarea
    */
    var initEvents = function (editorApi, toolstrip, alloy) {
      var tapping = TappingEvent.monitor(editorApi);
      var outerDoc = Traverse.owner(toolstrip);
      var isRanged = function (sel) {
        return !Compare.eq(sel.start(), sel.finish()) || sel.soffset() !== sel.foffset();
      };
      var hasRangeInUi = function () {
        return Focus.active(outerDoc).filter(function (input) {
          return Node.name(input) === 'input';
        }).exists(function (input) {
          return input.dom().selectionStart !== input.dom().selectionEnd;
        });
      };
      var updateMargin = function () {
        var rangeInContent = editorApi.doc().dom().hasFocus() && editorApi.getSelection().exists(isRanged);
        alloy.getByDom(toolstrip).each((rangeInContent || hasRangeInUi()) === true ? Toggling.on : Toggling.off);
      };
      var listeners = [
        DomEvent.bind(editorApi.body(), 'touchstart', function (evt) {
          editorApi.onTouchContent();
          tapping.fireTouchstart(evt);
        }),
        tapping.onTouchmove(),
        tapping.onTouchend(),
        DomEvent.bind(toolstrip, 'touchstart', function (evt) {
          editorApi.onTouchToolstrip();
        }),
        editorApi.onToReading(function () {
          Focus.blur(editorApi.body());
        }),
        editorApi.onToEditing(Fun.noop),
        // Scroll to cursor and update the iframe height
        editorApi.onScrollToCursor(function (tinyEvent) {
          tinyEvent.preventDefault();
          editorApi.getCursorBox().each(function (bounds) {
            var cWin = editorApi.win();
            // The goal here is to shift as little as required.
            var isOutside = bounds.top() > cWin.innerHeight || bounds.bottom() > cWin.innerHeight;
            var cScrollBy = isOutside ? bounds.bottom() - cWin.innerHeight + 50/*EXTRA_SPACING*/ : 0;
            if (cScrollBy !== 0) {
              cWin.scrollTo(cWin.pageXOffset, cWin.pageYOffset + cScrollBy);
            }         
          });
        })
      ].concat(
        isAndroid6 === true ? [ ] : [
          DomEvent.bind(Element.fromDom(editorApi.win()), 'blur', function () {
            alloy.getByDom(toolstrip).each(Toggling.off);
          }),
          DomEvent.bind(outerDoc, 'select', updateMargin),
          DomEvent.bind(editorApi.doc(), 'selectionchange', updateMargin)
        ]
      );
      var destroy = function () {
        Arr.each(listeners, function (l) {
          l.unbind();
        });
      };
      return {
        destroy: destroy
      };
    };
    return {
      initEvents: initEvents
    };
  }
);
 |