@@ -9,9 +9,10 @@ import * as keyboardEvent from 'vs/base/browser/keyboardEvent';
99import { isChrome , isWebKit } from 'vs/base/browser/browser' ;
1010import types = require( 'vs/base/common/types' ) ;
1111import { EventEmitter } from 'vs/base/common/eventEmitter' ;
12- import { IDisposable } from 'vs/base/common/lifecycle' ;
12+ import { Disposable , IDisposable } from 'vs/base/common/lifecycle' ;
1313import { onUnexpectedError } from 'vs/base/common/errors' ;
1414import browserService = require( 'vs/base/browser/browserService' ) ;
15+ import { TimeoutTimer } from 'vs/base/common/async' ;
1516
1617export type IKeyboardEvent = keyboardEvent . IKeyboardEvent ;
1718export type IMouseEvent = mouseEvent . IMouseEvent ;
@@ -178,45 +179,58 @@ export function toggleClass(node: HTMLElement, className: string, shouldHaveIt?:
178179 }
179180}
180181
182+ class DomListener extends Disposable {
181183
184+ private _usedAddEventListener : boolean ;
185+ private _wrapHandler : ( e : any ) => void ;
186+ private _node : any ;
187+ private _type : string ;
188+ private _useCapture : boolean ;
182189
183- function _addListener ( node : Element , type : string , handler : ( event : any ) => void , useCapture ?: boolean ) : ( ) => void ;
184- function _addListener ( node : Window , type : string , handler : ( event : any ) => void , useCapture ?: boolean ) : ( ) => void ;
185- function _addListener ( node : Document , type : string , handler : ( event : any ) => void , useCapture ?: boolean ) : ( ) => void ;
186- function _addListener ( node : any , type : string , handler : ( event : any ) => void , useCapture ?: boolean ) : ( ) => void {
187- let wrapHandler = function ( e ) : void {
188- e = e || window . event ;
189- handler ( e ) ;
190- } ;
190+ constructor ( node : Element | Window | Document , type : string , handler : ( e : any ) => void , useCapture ?: boolean ) {
191+ super ( ) ;
191192
192- if ( typeof node . addEventListener === 'function' ) {
193- node . addEventListener ( type , wrapHandler , useCapture || false ) ;
194- return function ( ) {
195- if ( ! wrapHandler ) {
196- // Already removed
197- return ;
198- }
199- node . removeEventListener ( type , wrapHandler , useCapture || false ) ;
193+ this . _node = node ;
194+ this . _type = type ;
195+ this . _useCapture = ( useCapture || false ) ;
200196
201- // Prevent leakers from holding on to the dom node or handler func
202- wrapHandler = null ;
203- node = null ;
204- handler = null ;
197+ this . _wrapHandler = ( e ) => {
198+ e = e || window . event ;
199+ handler ( e ) ;
205200 } ;
201+
202+ if ( typeof this . _node . addEventListener === 'function' ) {
203+ this . _usedAddEventListener = true ;
204+ this . _node . addEventListener ( this . _type , this . _wrapHandler , this . _useCapture ) ;
205+ } else {
206+ this . _usedAddEventListener = false ;
207+ this . _node . attachEvent ( 'on' + this . _type , this . _wrapHandler ) ;
208+ }
206209 }
207210
208- node . attachEvent ( 'on' + type , wrapHandler ) ;
209- return function ( ) { node . detachEvent ( 'on' + type , wrapHandler ) ; } ;
211+ public dispose ( ) : void {
212+ if ( ! this . _wrapHandler ) {
213+ // Already disposed
214+ return ;
215+ }
216+
217+ if ( this . _usedAddEventListener ) {
218+ this . _node . removeEventListener ( this . _type , this . _wrapHandler , this . _useCapture ) ;
219+ } else {
220+ this . _node . detachEvent ( 'on' + this . _type , this . _wrapHandler ) ;
221+ }
222+
223+ // Prevent leakers from holding on to the dom or handler func
224+ this . _node = null ;
225+ this . _wrapHandler = null ;
226+ }
210227}
211228
212229export function addDisposableListener ( node : Element , type : string , handler : ( event : any ) => void , useCapture ?: boolean ) : IDisposable ;
213230export function addDisposableListener ( node : Window , type : string , handler : ( event : any ) => void , useCapture ?: boolean ) : IDisposable ;
214231export function addDisposableListener ( node : Document , type : string , handler : ( event : any ) => void , useCapture ?: boolean ) : IDisposable ;
215232export function addDisposableListener ( node : any , type : string , handler : ( event : any ) => void , useCapture ?: boolean ) : IDisposable {
216- let dispose = _addListener ( node , type , handler , useCapture ) ;
217- return {
218- dispose : dispose
219- } ;
233+ return new DomListener ( node , type , handler , useCapture ) ;
220234}
221235
222236export interface IAddStandardDisposableListenerSignature {
@@ -262,8 +276,8 @@ export let addStandardDisposableListener: IAddStandardDisposableListenerSignatur
262276 } ;
263277} ;
264278
265- export function addNonBubblingMouseOutListener ( node : Element , handler : ( event : any ) => void ) : ( ) => void {
266- return _addListener ( node , 'mouseout' , ( e : MouseEvent ) => {
279+ export function addDisposableNonBubblingMouseOutListener ( node : Element , handler : ( event : MouseEvent ) => void ) : IDisposable {
280+ return addDisposableListener ( node , 'mouseout' , ( e : MouseEvent ) => {
267281 // Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements
268282 let toElement = < Node > ( e . relatedTarget || e . toElement ) ;
269283 while ( toElement && toElement !== node ) {
@@ -276,12 +290,6 @@ export function addNonBubblingMouseOutListener(node: Element, handler: (event: a
276290 handler ( e ) ;
277291 } ) ;
278292}
279- export function addDisposableNonBubblingMouseOutListener ( node : Element , handler : ( event : MouseEvent ) => void ) : IDisposable {
280- let dispose = addNonBubblingMouseOutListener ( node , handler ) ;
281- return {
282- dispose : dispose
283- } ;
284- }
285293
286294const _animationFrame = ( function ( ) {
287295 let emulatedRequestAnimationFrame = ( callback : ( time : number ) => void ) : number => {
@@ -450,49 +458,38 @@ const DEFAULT_EVENT_MERGER: IEventMerger<Event> = function(lastEvent: Event, cur
450458 return currentEvent ;
451459} ;
452460
453- function timeoutThrottledListener < R > ( node : any , type : string , handler : ( event : R ) => void , eventMerger : IEventMerger < R > = < any > DEFAULT_EVENT_MERGER , minimumTimeMs : number = MINIMUM_TIME_MS ) : ( ) => void {
454- let lastEvent : R = null , lastHandlerTime = 0 , timeout = - 1 ;
461+ class TimeoutThrottledDomListener < R > extends Disposable {
455462
456- function invokeHandler ( ) : void {
457- timeout = - 1 ;
458- lastHandlerTime = ( new Date ( ) ) . getTime ( ) ;
459- handler ( lastEvent ) ;
460- lastEvent = null ;
461- } ;
463+ constructor ( node : any , type : string , handler : ( event : R ) => void , eventMerger : IEventMerger < R > = < any > DEFAULT_EVENT_MERGER , minimumTimeMs : number = MINIMUM_TIME_MS ) {
464+ super ( ) ;
462465
463- let unbinder = _addListener ( node , type , function ( e ) {
464- lastEvent = eventMerger ( lastEvent , e ) ;
465- let elapsedTime = ( new Date ( ) ) . getTime ( ) - lastHandlerTime ;
466+ let lastEvent = null ;
467+ let lastHandlerTime = 0 ;
468+ let timeout = this . _register ( new TimeoutTimer ( ) ) ;
466469
467- if ( elapsedTime >= minimumTimeMs ) {
468- if ( timeout !== - 1 ) {
469- window . clearTimeout ( timeout ) ;
470- }
471- invokeHandler ( ) ;
472- } else {
473- if ( timeout === - 1 ) {
474- timeout = window . setTimeout ( invokeHandler , minimumTimeMs - elapsedTime ) ;
475- }
476- }
477- } ) ;
470+ let invokeHandler = ( ) => {
471+ lastHandlerTime = ( new Date ( ) ) . getTime ( ) ;
472+ handler ( lastEvent ) ;
473+ lastEvent = null ;
474+ } ;
478475
479- return function ( ) {
480- if ( timeout !== - 1 ) {
481- window . clearTimeout ( timeout ) ;
482- }
483- unbinder ( ) ;
484- } ;
485- }
476+ this . _register ( addDisposableListener ( node , type , ( e ) => {
486477
487- export function _addThrottledListener < R > ( node : any , type : string , handler : ( event : R ) => void , eventMerger ?: IEventMerger < R > , minimumTimeMs ?: number ) : ( ) => void {
488- return timeoutThrottledListener ( node , type , handler , eventMerger , minimumTimeMs ) ;
478+ lastEvent = eventMerger ( lastEvent , e ) ;
479+ let elapsedTime = ( new Date ( ) ) . getTime ( ) - lastHandlerTime ;
480+
481+ if ( elapsedTime >= minimumTimeMs ) {
482+ timeout . cancel ( ) ;
483+ invokeHandler ( ) ;
484+ } else {
485+ timeout . setIfNotSet ( invokeHandler , minimumTimeMs - elapsedTime ) ;
486+ }
487+ } ) ) ;
488+ }
489489}
490490
491491export function addDisposableThrottledListener < R > ( node : any , type : string , handler : ( event : R ) => void , eventMerger ?: IEventMerger < R > , minimumTimeMs ?: number ) : IDisposable {
492- let dispose = _addThrottledListener ( node , type , handler , eventMerger , minimumTimeMs ) ;
493- return {
494- dispose : dispose
495- } ;
492+ return new TimeoutThrottledDomListener < R > ( node , type , handler , eventMerger , minimumTimeMs ) ;
496493}
497494
498495export function getComputedStyle ( el : HTMLElement ) : CSSStyleDeclaration {
@@ -857,8 +854,8 @@ export const EventHelper = {
857854} ;
858855
859856export interface IFocusTracker {
860- addBlurListener ( fn ) : ( ) => void ;
861- addFocusListener ( fn ) : ( ) => void ;
857+ addBlurListener ( fn : ( ) => void ) : IDisposable ;
858+ addFocusListener ( fn : ( ) => void ) : IDisposable ;
862859 dispose ( ) : void ;
863860}
864861
@@ -897,56 +894,54 @@ export function restoreParentsScrollTop(node: Element, state: number[]): void {
897894 }
898895}
899896
900- export function trackFocus ( element : HTMLElement ) : IFocusTracker {
897+ class FocusTracker extends Disposable implements IFocusTracker {
901898
902- let hasFocus : boolean = false , loosingFocus = false ;
903- let eventEmitter = new EventEmitter ( ) , unbind = [ ] , result : IFocusTracker = null ;
899+ private _eventEmitter : EventEmitter ;
904900
905- // fill result
906- result = {
907- addFocusListener : function ( fn ) {
908- let h = eventEmitter . addListener ( 'focus' , fn ) ;
909- unbind . push ( h ) ;
910- return h ;
911- } ,
912- addBlurListener : function ( fn ) {
913- let h = eventEmitter . addListener ( 'blur' , fn ) ;
914- unbind . push ( h ) ;
915- return h ;
916- } ,
917- dispose : function ( ) {
918- while ( unbind . length > 0 ) {
919- unbind . pop ( ) ( ) ;
901+ constructor ( element : HTMLElement ) {
902+ super ( ) ;
903+
904+ let hasFocus = false ;
905+ let loosingFocus = false ;
906+
907+ this . _eventEmitter = this . _register ( new EventEmitter ( ) ) ;
908+
909+ let onFocus = ( event ) => {
910+ loosingFocus = false ;
911+ if ( ! hasFocus ) {
912+ hasFocus = true ;
913+ this . _eventEmitter . emit ( 'focus' , { } ) ;
920914 }
921- }
922- } ;
915+ } ;
923916
924- let onFocus = function ( event ) {
925- loosingFocus = false ;
926- if ( ! hasFocus ) {
927- hasFocus = true ;
928- eventEmitter . emit ( 'focus' , { } ) ;
929- }
930- } ;
917+ let onBlur = ( event ) => {
918+ if ( hasFocus ) {
919+ loosingFocus = true ;
920+ window . setTimeout ( ( ) => {
921+ if ( loosingFocus ) {
922+ loosingFocus = false ;
923+ hasFocus = false ;
924+ this . _eventEmitter . emit ( 'blur' , { } ) ;
925+ }
926+ } , 0 ) ;
927+ }
928+ } ;
931929
932- let onBlur = function ( event ) {
933- if ( hasFocus ) {
934- loosingFocus = true ;
935- window . setTimeout ( function ( ) {
936- if ( loosingFocus ) {
937- loosingFocus = false ;
938- hasFocus = false ;
939- eventEmitter . emit ( 'blur' , { } ) ;
940- }
941- } , 0 ) ;
942- }
943- } ;
930+ this . _register ( addDisposableListener ( element , EventType . FOCUS , onFocus , true ) ) ;
931+ this . _register ( addDisposableListener ( element , EventType . BLUR , onBlur , true ) ) ;
932+ }
944933
945- // bind
946- unbind . push ( _addListener ( element , EventType . FOCUS , onFocus , true ) ) ;
947- unbind . push ( _addListener ( element , EventType . BLUR , onBlur , true ) ) ;
934+ public addFocusListener ( fn : ( ) => void ) : IDisposable {
935+ return this . _eventEmitter . addListener2 ( 'focus' , fn ) ;
936+ }
948937
949- return result ;
938+ public addBlurListener ( fn :( ) => void ) : IDisposable {
939+ return this . _eventEmitter . addListener2 ( 'blur' , fn ) ;
940+ }
941+ }
942+
943+ export function trackFocus ( element : HTMLElement ) : IFocusTracker {
944+ return new FocusTracker ( element ) ;
950945}
951946
952947export function removeScriptTags ( html : string ) : string {
0 commit comments