function attachToDocument($mdGesture, $$MdGestureHandler, $mdUtil) {
  if (disableAllGestures) {
    return;
  }

  if (!isInitialized && $mdGesture.isHijackingClicks) {
    /*
     * If hijack clicks is true, we preventDefault any click that wasn't
     * sent by AngularJS Material. This is because on older Android & iOS, a false, or 'ghost',
     * click event will be sent ~400ms after a touchend event happens.
     * The only way to know if this click is real is to prevent any normal
     * click events, and add a flag to events sent by material so we know not to prevent those.
     *
     * Two exceptions to click events that should be prevented are:
     *  - click events sent by the keyboard (eg form submit)
     *  - events that originate from an Ionic app
     */
    document.addEventListener('click'    , clickHijacker     , true);
    document.addEventListener('mouseup'  , mouseInputHijacker, true);
    document.addEventListener('mousedown', mouseInputHijacker, true);
    document.addEventListener('focus'    , mouseInputHijacker, true);

    isInitialized = true;
  }

  function mouseInputHijacker(ev) {
    var isKeyClick = !ev.clientX && !ev.clientY;

    if (
      !isKeyClick &&
      !ev.$material &&
      !ev.isIonicTap &&
      !isInputEventFromLabelClick(ev) &&
      (ev.type !== 'mousedown' || (!canFocus(ev.target) && !canFocus(document.activeElement)))
    ) {
      ev.preventDefault();
      ev.stopPropagation();
    }
  }

  /**
   * Ignore click events that don't come from AngularJS Material, Ionic, Input Label clicks,
   * or key presses that generate click events. This helps to ignore the ghost tap events on
   * older mobile browsers that get sent after a 300-400ms delay.
   * @param ev MouseEvent or modified MouseEvent with $material, pointer, and other fields
   */
  function clickHijacker(ev) {
    var isKeyClick;
    if ($mdUtil.isIos) {
      isKeyClick = angular.isDefined(ev.webkitForce) && ev.webkitForce === 0;
    } else {
      isKeyClick = ev.clientX === 0 && ev.clientY === 0;
    }
    if (!isKeyClick && !ev.$material && !ev.isIonicTap && !isInputEventFromLabelClick(ev)) {
      ev.preventDefault();
      ev.stopPropagation();
      lastLabelClickPos = null;
    } else {
      lastLabelClickPos = null;
      if (ev.target.tagName.toLowerCase() === 'label') {
        lastLabelClickPos = {x: ev.x, y: ev.y};
      }
    }
  }


  // Listen to all events to cover all platforms.
  var START_EVENTS = 'mousedown touchstart pointerdown';
  var MOVE_EVENTS = 'mousemove touchmove pointermove';
  var END_EVENTS = 'mouseup mouseleave touchend touchcancel pointerup pointercancel';

  angular.element(document)
    .on(START_EVENTS, gestureStart)
    .on(MOVE_EVENTS, gestureMove)
    .on(END_EVENTS, gestureEnd)
    // For testing
    .on('$$mdGestureReset', function gestureClearCache () {
      lastPointer = pointer = null;
    });

  /**
   * When a DOM event happens, run all registered gesture handlers' lifecycle
   * methods which match the DOM event.
   * Eg. when a 'touchstart' event happens, runHandlers('start') will call and
   * run `handler.cancel()` and `handler.start()` on all registered handlers.
   */
  function runHandlers(handlerEvent, event) {
    var handler;
    for (var name in HANDLERS) {
      handler = HANDLERS[name];
      if (handler instanceof $$MdGestureHandler) {

        if (handlerEvent === 'start') {
          // Run cancel to reset any handlers' state
          handler.cancel();
        }
        handler[handlerEvent](event, pointer);
      }
    }
  }

  /*
   * gestureStart vets if a start event is legitimate (and not part of a 'ghost click' from iOS/Android)
   * If it is legitimate, we initiate the pointer state and mark the current pointer's type
   * For example, for a touchstart event, mark the current pointer as a 'touch' pointer, so mouse events
   * won't effect it.
   */
  function gestureStart(ev) {
    // If we're already touched down, abort
    if (pointer) return;

    var now = +Date.now();

    // iOS & old android bug: after a touch event, a click event is sent 350 ms later.
    // If <400ms have passed, don't allow an event of a different type than the previous event
    if (lastPointer && !typesMatch(ev, lastPointer) && (now - lastPointer.endTime < 1500)) {
      return;
    }

    pointer = makeStartPointer(ev);

    runHandlers('start', ev);
  }

  /**
   * If a move event happens of the right type, update the pointer and run all the move handlers.
   * "of the right type": if a mousemove happens but our pointer started with a touch event, do
   * nothing.
   */
  function gestureMove(ev) {
    if (!pointer || !typesMatch(ev, pointer)) return;

    updatePointerState(ev, pointer);
    runHandlers('move', ev);
  }

  /**
   * If an end event happens of the right type, update the pointer, run endHandlers, and save the
   * pointer as 'lastPointer'.
   */
  function gestureEnd(ev) {
    if (!pointer || !typesMatch(ev, pointer)) return;

    updatePointerState(ev, pointer);
    pointer.endTime = +Date.now();

    if (ev.type !== 'pointercancel') {
      runHandlers('end', ev);
    }

    lastPointer = pointer;
    pointer = null;
  }

}
Attach Gestures: hook document and check shouldHijack clicks
@ngInject