import { Injectable } from '@angular/core' ; import { EventManager } from '@angular/platform-browser' ; import { noop } from '../framework/utils' ; const isDoc = ! ( typeof document === 'undefined' || ! document ) ; const eventMap = { clickOutside : 'click' , mousedownOutside : 'mousedown' , mouseupOutside : 'mouseup' , mousemoveOutside : 'mousemove' } ; param element param handler function bubbleNonAncestorHandlerFactory ( element , handler ) { return ( event ) => { let current = event . target ; do { if ( current === element ) { return ; } } while ( current . parentNode && ( current = current . parentNode ) ) ; handler ( event ) ; } ; } @ Injectable ( ) export class DOMOutsideEventPlugin { manager : EventManager ; constructor ( ) { if ( ! isDoc || typeof document . addEventListener !== 'function' ) { this . addEventListener = noop as any ; } } supports ( eventName ) { return eventMap . hasOwnProperty ( eventName ) ; } addEventListener ( element , eventName , handler ) { const zone = this . manager . getZone ( ) ; const onceOnOutside = ( ) => { const listener = bubbleNonAncestorHandlerFactory ( element , evt => zone . runGuarded ( ( ) => handler ( evt ) ) ) ; document . addEventListener ( eventMap [ eventName ] , listener , false ) ; return ( ) => document . removeEventListener ( eventMap [ eventName ] , listener , false ) ; } ; return zone . runOutsideAngular ( ( ) => { let fn ; setTimeout ( ( ) => fn = onceOnOutside ( ) , 0 ) ; return ( ) => { if ( fn ) fn ( ) ; } ; } ) ; } }