import { Component , Directive , ElementRef , Renderer2 , EventEmitter , ComponentFactoryResolver , Host , ViewEncapsulation , Type , ComponentRef , KeyValueDiffer , KeyValueDiffers , OnInit , OnDestroy , DoCheck , ViewContainerRef , Output } from '@angular/core' ; import { NgGridConfig , NgGridItemEvent , NgGridItemPosition , NgGridItemSize , NgGridRawPosition , NgGridItemDimensions , NgConfigFixDirection } from '../interfaces/INgGrid' ; import { NgGridItem } from './NgGridItem' ; import * as NgGridHelper from '../helpers/NgGridHelpers' ; import { NgGridPlaceholder } from '../components/NgGridPlaceholder' ; import { Subscription , Observable , fromEvent } from 'rxjs' ; @ Directive ( { selector : '[ngGrid]' , inputs : [ 'config: ngGrid' ] , host : { '(window:resize)' : 'resizeEventHandler($event)' , } } ) export class NgGrid implements OnInit , DoCheck , OnDestroy { public static CONST_DEFAULT_RESIZE_DIRECTIONS : string [ ] = [ 'bottomright' , 'bottomleft' , 'topright' , 'topleft' , 'right' , 'left' , 'bottom' , 'top' , ] ; @ Output ( ) public onDragStart : EventEmitter < NgGridItem > = new EventEmitter < NgGridItem > ( ) ; @ Output ( ) public onDrag : EventEmitter < NgGridItem > = new EventEmitter < NgGridItem > ( ) ; @ Output ( ) public onDragStop : EventEmitter < NgGridItem > = new EventEmitter < NgGridItem > ( ) ; @ Output ( ) public onResizeStart : EventEmitter < NgGridItem > = new EventEmitter < NgGridItem > ( ) ; @ Output ( ) public onResize : EventEmitter < NgGridItem > = new EventEmitter < NgGridItem > ( ) ; @ Output ( ) public onResizeStop : EventEmitter < NgGridItem > = new EventEmitter < NgGridItem > ( ) ; @ Output ( ) public onItemChange : EventEmitter < Array < NgGridItemEvent > > = new EventEmitter < Array < NgGridItemEvent > > ( ) ; public colWidth : number = 250 ; public rowHeight : number = 250 ; public minCols : number = 1 ; public minRows : number = 1 ; public marginTop : number = 10 ; public marginRight : number = 10 ; public marginBottom : number = 10 ; public marginLeft : number = 10 ; public screenMargin : number = 0 ; public isDragging : boolean = false ; public isResizing : boolean = false ; public autoStyle : boolean = true ; public resizeEnable : boolean = true ; public dragEnable : boolean = true ; public cascade : string = 'up' ; public minWidth : number = 100 ; public minHeight : number = 100 ; public resizeDirections : string [ ] = NgGrid . CONST_DEFAULT_RESIZE_DIRECTIONS ; private _items : Map < string , NgGridItem > = new Map < string , NgGridItem > ( ) ; private _draggingItem : NgGridItem = null ; private _resizingItem : NgGridItem = null ; private _resizeDirection : string = null ; private _itemsInGrid : Set < string > = new Set < string > ( ) ; private _containerWidth : number ; private _containerHeight : number ; private _maxCols : number = 0 ; private _maxRows : number = 0 ; private _visibleCols : number = 0 ; private _visibleRows : number = 0 ; private _setWidth : number = 250 ; private _setHeight : number = 250 ; private _posOffset : NgGridRawPosition = null ; private _adding : boolean = false ; private _placeholderRef : ComponentRef < NgGridPlaceholder > = null ; private _fixToGrid : boolean = false ; private _autoResize : boolean = false ; private _differ : KeyValueDiffer < string , any > ; private _destroyed : boolean = false ; private _maintainRatio : boolean = false ; private _aspectRatio : number ; private _preferNew : boolean = false ; private _zoomOnDrag : boolean = false ; private _limitToScreen : boolean = false ; private _centerToScreen : boolean = false ; private _curMaxRow : number = 0 ; private _curMaxCol : number = 0 ; private _dragReady : boolean = false ; private _resizeReady : boolean = false ; private _elementBasedDynamicRowHeight : boolean = false ; private _itemFixDirection : NgConfigFixDirection = 'cascade' ; private _collisionFixDirection : NgConfigFixDirection = 'cascade' ; private _allowOverlap : boolean = false ; private _cascadePromise : Promise < void > ; private _lastZValue : number = 1 ; private _documentMousemove$ : Observable < MouseEvent > ; private _documentMouseup$ : Observable < MouseEvent > ; private _mousedown$ : Observable < MouseEvent > ; private _mousemove$ : Observable < MouseEvent > ; private _mouseup$ : Observable < MouseEvent > ; private _touchstart$ : Observable < TouchEvent > ; private _touchmove$ : Observable < TouchEvent > ; private _touchend$ : Observable < TouchEvent > ; private _subscriptions : Subscription [ ] = [ ] ; private _enabledListener : boolean = false ; private static CONST_DEFAULT_CONFIG : NgGridConfig = { margins : [ 10 ] , draggable : true , resizable : true , max_cols : 0 , max_rows : 0 , visible_cols : 0 , visible_rows : 0 , col_width : 250 , row_height : 250 , cascade : 'up' , min_width : 100 , min_height : 100 , fix_to_grid : false , auto_style : true , auto_resize : false , maintain_ratio : false , prefer_new : false , zoom_on_drag : false , limit_to_screen : false , center_to_screen : false , resize_directions : NgGrid . CONST_DEFAULT_RESIZE_DIRECTIONS , element_based_row_height : false , fix_item_position_direction : 'cascade' , fix_collision_position_direction : 'cascade' , allow_overlap : false , } ; private _config = NgGrid . CONST_DEFAULT_CONFIG ; set config ( v ) { if ( v == null || typeof v !== 'object' ) { return ; } this . setConfig ( v ) ; if ( this . _differ == null && v != null ) { this . _differ = this . _differs . find ( this . _config ) . create ( ) ; } this . _differ . diff ( this . _config ) ; } constructor ( private _differs , private _ngEl , private _renderer , private componentFactoryResolver ) { this . _defineListeners ( ) ; } public ngOnInit ( ) { this . _renderer . addClass ( this . _ngEl . nativeElement , 'grid' ) ; if ( this . autoStyle ) this . _renderer . setStyle ( this . _ngEl . nativeElement , 'position' , 'relative' ) ; this . setConfig ( this . _config ) ; } public ngOnDestroy ( ) { this . _destroyed = true ; this . _disableListeners ( ) ; } public generateItemUid ( ) { const uid = NgGridHelper . generateUuid ( ) ; if ( this . _items . has ( uid ) ) { return this . generateItemUid ( ) ; } return uid ; } public setConfig ( config ) { this . _config = config ; var maxColRowChanged = false ; for ( var x in config ) { var val = config [ x ] ; var intVal = ! val ? 0 : parseInt ( val ) ; switch ( x ) { case 'margins' : this . setMargins ( val ) ; break ; case 'col_width' : this . colWidth = Math . max ( intVal , 1 ) ; break ; case 'row_height' : this . rowHeight = Math . max ( intVal , 1 ) ; break ; case 'auto_style' : this . autoStyle = val ? true : false ; break ; case 'auto_resize' : this . _autoResize = val ? true : false ; break ; case 'draggable' : this . dragEnable = val ? true : false ; break ; case 'resizable' : this . resizeEnable = val ? true : false ; break ; case 'max_rows' : maxColRowChanged = maxColRowChanged || this . _maxRows != intVal ; this . _maxRows = intVal < 0 ? 0 : intVal ; break ; case 'max_cols' : maxColRowChanged = maxColRowChanged || this . _maxCols != intVal ; this . _maxCols = intVal < 0 ? 0 : intVal ; break ; case 'visible_rows' : this . _visibleRows = Math . max ( intVal , 0 ) ; break ; case 'visible_cols' : this . _visibleCols = Math . max ( intVal , 0 ) ; break ; case 'min_rows' : this . minRows = Math . max ( intVal , 1 ) ; break ; case 'min_cols' : this . minCols = Math . max ( intVal , 1 ) ; break ; case 'min_height' : this . minHeight = Math . max ( intVal , 1 ) ; break ; case 'min_width' : this . minWidth = Math . max ( intVal , 1 ) ; break ; case 'zoom_on_drag' : this . _zoomOnDrag = val ? true : false ; break ; case 'cascade' : if ( this . cascade != val ) { this . cascade = val ; this . _cascadeGrid ( ) ; } break ; case 'fix_to_grid' : this . _fixToGrid = val ? true : false ; break ; case 'maintain_ratio' : this . _maintainRatio = val ? true : false ; break ; case 'prefer_new' : this . _preferNew = val ? true : false ; break ; case 'limit_to_screen' : this . _limitToScreen = ! this . _autoResize && ! ! val ; break ; case 'center_to_screen' : this . _centerToScreen = val ? true : false ; break ; case 'resize_directions' : this . resizeDirections = val || [ 'bottomright' , 'bottomleft' , 'topright' , 'topleft' , 'right' , 'left' , 'bottom' , 'top' ] ; break ; case 'element_based_row_height' : this . _elementBasedDynamicRowHeight = ! ! val ; break ; case 'fix_item_position_direction' : this . _itemFixDirection = val ; break ; case 'fix_collision_position_direction' : this . _collisionFixDirection = val ; break ; case 'allow_overlap' : this . _allowOverlap = ! ! val ; break ; } } if ( this . _allowOverlap && this . cascade !== 'off' && this . cascade !== '' ) { console . warn ( 'Unable to overlap items when a cascade direction is set.' ) ; this . _allowOverlap = false ; } if ( this . dragEnable || this . resizeEnable ) { this . _enableListeners ( ) ; } else { this . _disableListeners ( ) ; } if ( this . _itemFixDirection === 'cascade' ) { this . _itemFixDirection = this . _getFixDirectionFromCascade ( ) ; } if ( this . _collisionFixDirection === 'cascade' ) { this . _collisionFixDirection = this . _getFixDirectionFromCascade ( ) ; } if ( this . _limitToScreen ) { const newMaxCols = this . _getContainerColumns ( ) ; if ( this . _maxCols != newMaxCols ) { this . _maxCols = newMaxCols ; maxColRowChanged = true ; } } if ( this . _limitToScreen && this . _centerToScreen ) { this . screenMargin = this . _getScreenMargin ( ) ; } else { this . screenMargin = 0 ; } if ( this . _maintainRatio ) { if ( this . colWidth && this . rowHeight ) { this . _aspectRatio = this . colWidth / this . rowHeight ; } else { this . _maintainRatio = false ; } } if ( maxColRowChanged ) { if ( this . _maxCols > 0 && this . _maxRows > 0 ) { switch ( this . cascade ) { case 'left' : case 'right' : this . _maxCols = 0 ; break ; case 'up' : case 'down' : default : this . _maxRows = 0 ; break ; } } this . _updatePositionsAfterMaxChange ( ) ; } this . _calculateColWidth ( ) ; this . _calculateRowHeight ( ) ; var maxWidth = this . _maxCols * this . colWidth ; var maxHeight = this . _maxRows * this . rowHeight ; if ( maxWidth > 0 && this . minWidth > maxWidth ) this . minWidth = 0.75 * this . colWidth ; if ( maxHeight > 0 && this . minHeight > maxHeight ) this . minHeight = 0.75 * this . rowHeight ; if ( this . minWidth > this . colWidth ) this . minCols = Math . max ( this . minCols , Math . ceil ( this . minWidth / this . colWidth ) ) ; if ( this . minHeight > this . rowHeight ) this . minRows = Math . max ( this . minRows , Math . ceil ( this . minHeight / this . rowHeight ) ) ; if ( this . _maxCols > 0 && this . minCols > this . _maxCols ) this . minCols = 1 ; if ( this . _maxRows > 0 && this . minRows > this . _maxRows ) this . minRows = 1 ; this . _updateRatio ( ) ; this . _items . forEach ( ( item ) => { this . _removeFromGrid ( item ) ; item . setCascadeMode ( this . cascade ) ; } ) ; this . _items . forEach ( ( item ) => { item . recalculateSelf ( ) ; this . _addToGrid ( item ) ; } ) ; this . _cascadeGrid ( ) ; this . _updateSize ( ) ; } public getItemPosition ( itemId ) { return this . _items . has ( itemId ) ? this . _items . get ( itemId ) . getGridPosition ( ) : null ; } public getItemSize ( itemId ) { return this . _items . has ( itemId ) ? this . _items . get ( itemId ) . getSize ( ) : null ; } public ngDoCheck ( ) { if ( this . _differ != null ) { var changes = this . _differ . diff ( this . _config ) ; if ( changes != null ) { this . _applyChanges ( changes ) ; return true ; } } return false ; } public setMargins ( margins ) { this . marginTop = Math . max ( parseInt ( margins [ 0 ] ) , 0 ) ; this . marginRight = margins . length >= 2 ? Math . max ( parseInt ( margins [ 1 ] ) , 0 ) : this . marginTop ; this . marginBottom = margins . length >= 3 ? Math . max ( parseInt ( margins [ 2 ] ) , 0 ) : this . marginTop ; this . marginLeft = margins . length >= 4 ? Math . max ( parseInt ( margins [ 3 ] ) , 0 ) : this . marginRight ; } public enableDrag ( ) { this . dragEnable = true ; } public disableDrag ( ) { this . dragEnable = false ; } public enableResize ( ) { this . resizeEnable = true ; } public disableResize ( ) { this . resizeEnable = false ; } public addItem ( ngItem ) { ngItem . setCascadeMode ( this . cascade ) ; if ( ! this . _preferNew ) { var newPos = this . _fixGridPosition ( ngItem . getGridPosition ( ) , ngItem . getSize ( ) ) ; ngItem . setGridPosition ( newPos ) ; } if ( ngItem . uid === null || this . _items . has ( ngItem . uid ) ) { ngItem . uid = this . generateItemUid ( ) ; } this . _items . set ( ngItem . uid , ngItem ) ; this . _addToGrid ( ngItem ) ; this . _updateSize ( ) ; this . triggerCascade ( ) . then ( ( ) => { ngItem . recalculateSelf ( ) ; ngItem . onCascadeEvent ( ) ; this . _emitOnItemChange ( ) ; } ) ; } public removeItem ( ngItem ) { this . _removeFromGrid ( ngItem ) ; this . _items . delete ( ngItem . uid ) ; if ( this . _destroyed ) return ; this . triggerCascade ( ) . then ( ( ) => { this . _updateSize ( ) ; this . _items . forEach ( ( item ) => item . recalculateSelf ( ) ) ; this . _emitOnItemChange ( ) ; } ) ; } public updateItem ( ngItem ) { this . _removeFromGrid ( ngItem ) ; this . _addToGrid ( ngItem ) ; this . triggerCascade ( ) . then ( ( ) => { this . _updateSize ( ) ; ngItem . onCascadeEvent ( ) ; } ) ; } public triggerCascade ( ) { if ( ! this . _cascadePromise ) { this . _cascadePromise = new Promise < void > ( ( resolve ) => { setTimeout ( ( ) => { this . _cascadePromise = null ; this . _cascadeGrid ( null , null ) ; resolve ( ) ; } , 0 ) ; } ) ; } return this . _cascadePromise ; } public triggerResize ( ) { this . resizeEventHandler ( null ) ; } public resizeEventHandler ( e ) { this . _calculateColWidth ( ) ; this . _calculateRowHeight ( ) ; this . _updateRatio ( ) ; if ( this . _limitToScreen ) { const newMaxColumns = this . _getContainerColumns ( ) ; if ( this . _maxCols !== newMaxColumns ) { this . _maxCols = newMaxColumns ; this . _updatePositionsAfterMaxChange ( ) ; this . _cascadeGrid ( ) ; } if ( this . _centerToScreen ) { this . screenMargin = this . _getScreenMargin ( ) ; this . _items . forEach ( ( item ) => { item . recalculateSelf ( ) ; } ) ; } } else if ( this . _autoResize ) { this . _items . forEach ( ( item ) => { item . recalculateSelf ( ) ; } ) ; } this . _updateSize ( ) ; } public mouseDownEventHandler ( e ) { var mousePos = this . _getMousePosition ( e ) ; var item = this . _getItemFromPosition ( mousePos ) ; if ( item == null ) return ; const resizeDirection = item . canResize ( e ) ; if ( this . resizeEnable && resizeDirection ) { this . _resizeReady = true ; this . _resizingItem = item ; this . _resizeDirection = resizeDirection ; e . preventDefault ( ) ; } else if ( this . dragEnable && item . canDrag ( e ) ) { this . _dragReady = true ; this . _draggingItem = item ; const itemPos = item . getPosition ( ) ; this . _posOffset = { 'left' : ( mousePos . left - itemPos . left ) , 'top' : ( mousePos . top - itemPos . top ) } ; e . preventDefault ( ) ; } } public mouseUpEventHandler ( e ) { if ( this . isDragging ) { this . _dragStop ( e ) ; } else if ( this . isResizing ) { this . _resizeStop ( e ) ; } else if ( this . _dragReady || this . _resizeReady ) { this . _cleanDrag ( ) ; this . _cleanResize ( ) ; } } public mouseMoveEventHandler ( e ) { if ( this . _resizeReady ) { this . _resizeStart ( e ) ; e . preventDefault ( ) ; return ; } else if ( this . _dragReady ) { this . _dragStart ( e ) ; e . preventDefault ( ) ; return ; } if ( this . isDragging ) { this . _drag ( e ) ; } else if ( this . isResizing ) { this . _resize ( e ) ; } else { var mousePos = this . _getMousePosition ( e ) ; var item = this . _getItemFromPosition ( mousePos ) ; if ( item ) { item . onMouseMove ( e ) ; } } } private _getFixDirectionFromCascade ( ) { switch ( this . cascade ) { case 'up' : case 'down' : default : return 'vertical' ; case 'left' : case 'right' : return 'horizontal' ; } } private _updatePositionsAfterMaxChange ( ) { this . _items . forEach ( ( item ) => { var pos = item . getGridPosition ( ) ; var dims = item . getSize ( ) ; if ( ! this . _hasGridCollision ( pos , dims ) && this . _isWithinBounds ( pos , dims ) && dims . x <= this . _maxCols && dims . y <= this . _maxRows ) { return ; } this . _removeFromGrid ( item ) ; if ( this . _maxCols > 0 && dims . x > this . _maxCols ) { dims . x = this . _maxCols ; item . setSize ( dims ) ; } else if ( this . _maxRows > 0 && dims . y > this . _maxRows ) { dims . y = this . _maxRows ; item . setSize ( dims ) ; } if ( this . _hasGridCollision ( pos , dims ) || ! this . _isWithinBounds ( pos , dims , true ) ) { var newPosition = this . _fixGridPosition ( pos , dims ) ; item . setGridPosition ( newPosition ) ; } this . _addToGrid ( item ) ; } ) ; } private _calculateColWidth ( ) { if ( this . _autoResize ) { if ( this . _maxCols > 0 || this . _visibleCols > 0 ) { var maxCols = this . _maxCols > 0 ? this . _maxCols : this . _visibleCols ; var maxWidth = this . _ngEl . nativeElement . getBoundingClientRect ( ) . width ; var colWidth = Math . floor ( maxWidth / maxCols ) ; colWidth -= ( this . marginLeft + this . marginRight ) ; if ( colWidth > 0 ) this . colWidth = colWidth ; } } if ( this . colWidth < this . minWidth || this . minCols > this . _config . min_cols ) { this . minCols = Math . max ( this . _config . min_cols , Math . ceil ( this . minWidth / this . colWidth ) ) ; } } private _calculateRowHeight ( ) { if ( this . _autoResize ) { if ( this . _maxRows > 0 || this . _visibleRows > 0 ) { var maxRows = this . _maxRows > 0 ? this . _maxRows : this . _visibleRows ; let maxHeight ; if ( this . _elementBasedDynamicRowHeight ) { maxHeight = this . _ngEl . nativeElement . getBoundingClientRect ( ) . height ; } else { maxHeight = window . innerHeight - this . marginTop - this . marginBottom ; } var rowHeight = Math . max ( Math . floor ( maxHeight / maxRows ) , this . minHeight ) ; rowHeight -= ( this . marginTop + this . marginBottom ) ; if ( rowHeight > 0 ) this . rowHeight = rowHeight ; } } if ( this . rowHeight < this . minHeight || this . minRows > this . _config . min_rows ) { this . minRows = Math . max ( this . _config . min_rows , Math . ceil ( this . minHeight / this . rowHeight ) ) ; } } private _updateRatio ( ) { if ( ! this . _autoResize || ! this . _maintainRatio ) return ; if ( this . _maxCols > 0 && this . _visibleRows <= 0 ) { this . rowHeight = this . colWidth / this . _aspectRatio ; } else if ( this . _maxRows > 0 && this . _visibleCols <= 0 ) { this . colWidth = this . _aspectRatio * this . rowHeight ; } else if ( this . _maxCols == 0 && this . _maxRows == 0 ) { if ( this . _visibleCols > 0 ) { this . rowHeight = this . colWidth / this . _aspectRatio ; } else if ( this . _visibleRows > 0 ) { this . colWidth = this . _aspectRatio * this . rowHeight ; } } } private _applyChanges ( changes ) { changes . forEachAddedItem ( ( record ) => { this . _config [ record . key ] = record . currentValue ; } ) ; changes . forEachChangedItem ( ( record ) => { this . _config [ record . key ] = record . currentValue ; } ) ; changes . forEachRemovedItem ( ( record ) => { delete this . _config [ record . key ] ; } ) ; this . setConfig ( this . _config ) ; } private _resizeStart ( e ) { if ( ! this . resizeEnable || ! this . _resizingItem ) return ; this . _resizingItem . startMoving ( ) ; this . _removeFromGrid ( this . _resizingItem ) ; this . _createPlaceholder ( this . _resizingItem ) ; if ( this . _allowOverlap ) { this . _resizingItem . zIndex = this . _lastZValue ++ ; } this . isResizing = true ; this . _resizeReady = false ; this . onResizeStart . emit ( this . _resizingItem ) ; this . _resizingItem . onResizeStartEvent ( ) ; } private _dragStart ( e ) { if ( ! this . dragEnable || ! this . _draggingItem ) return ; this . _draggingItem . startMoving ( ) ; this . _removeFromGrid ( this . _draggingItem ) ; this . _createPlaceholder ( this . _draggingItem ) ; if ( this . _allowOverlap ) { this . _draggingItem . zIndex = this . _lastZValue ++ ; } this . isDragging = true ; this . _dragReady = false ; this . onDragStart . emit ( this . _draggingItem ) ; this . _draggingItem . onDragStartEvent ( ) ; if ( this . _zoomOnDrag ) { this . _zoomOut ( ) ; } } private _zoomOut ( ) { this . _renderer . setStyle ( this . _ngEl . nativeElement , 'transform' , 'scale(0.5, 0.5)' ) ; } private _resetZoom ( ) { this . _renderer . setStyle ( this . _ngEl . nativeElement , 'transform' , '' ) ; } private _drag ( e ) { if ( ! this . isDragging ) return ; if ( window . getSelection ) { if ( window . getSelection ( ) . empty ) { window . getSelection ( ) . empty ( ) ; } else if ( window . getSelection ( ) . removeAllRanges ) { window . getSelection ( ) . removeAllRanges ( ) ; } } else if ( ( < any > document ) . selection ) { ( < any > document ) . selection . empty ( ) ; } var mousePos = this . _getMousePosition ( e ) ; var newL = ( mousePos . left - this . _posOffset . left ) ; var newT = ( mousePos . top - this . _posOffset . top ) ; var itemPos = this . _draggingItem . getGridPosition ( ) ; var gridPos = this . _calculateGridPosition ( newL , newT ) ; var dims = this . _draggingItem . getSize ( ) ; gridPos = this . _fixPosToBoundsX ( gridPos , dims ) ; if ( ! this . _isWithinBoundsY ( gridPos , dims ) ) { gridPos = this . _fixPosToBoundsY ( gridPos , dims ) ; } if ( gridPos . col != itemPos . col || gridPos . row != itemPos . row ) { this . _draggingItem . setGridPosition ( gridPos , this . _fixToGrid ) ; this . _placeholderRef . instance . setGridPosition ( gridPos ) ; if ( [ 'up' , 'down' , 'left' , 'right' ] . indexOf ( this . cascade ) >= 0 ) { this . _fixGridCollisions ( gridPos , dims ) ; this . _cascadeGrid ( gridPos , dims ) ; } } if ( ! this . _fixToGrid ) { this . _draggingItem . setPosition ( newL , newT ) ; } this . onDrag . emit ( this . _draggingItem ) ; this . _draggingItem . onDragEvent ( ) ; } private _resize ( e ) { if ( ! this . isResizing ) { return ; } if ( window . getSelection ) { if ( window . getSelection ( ) . empty ) { window . getSelection ( ) . empty ( ) ; } else if ( window . getSelection ( ) . removeAllRanges ) { window . getSelection ( ) . removeAllRanges ( ) ; } } else if ( ( < any > document ) . selection ) { ( < any > document ) . selection . empty ( ) ; } const mousePos = this . _getMousePosition ( e ) ; const itemPos = this . _resizingItem . getPosition ( ) ; const itemDims = this . _resizingItem . getDimensions ( ) ; const endCorner = { left : itemPos . left + itemDims . width , top : itemPos . top + itemDims . height , } ; const resizeTop = this . _resizeDirection . includes ( 'top' ) ; const resizeBottom = this . _resizeDirection . includes ( 'bottom' ) ; const resizeLeft = this . _resizeDirection . includes ( 'left' ) ; const resizeRight = this . _resizeDirection . includes ( 'right' ) ; let newW = resizeRight ? ( mousePos . left - itemPos . left + 1 ) : resizeLeft ? ( endCorner . left - mousePos . left + 1 ) : itemDims . width ; let newH = resizeBottom ? ( mousePos . top - itemPos . top + 1 ) : resizeTop ? ( endCorner . top - mousePos . top + 1 ) : itemDims . height ; if ( newW < this . minWidth ) newW = this . minWidth ; if ( newH < this . minHeight ) newH = this . minHeight ; if ( newW < this . _resizingItem . minWidth ) newW = this . _resizingItem . minWidth ; if ( newH < this . _resizingItem . minHeight ) newH = this . _resizingItem . minHeight ; let newX = itemPos . left ; let newY = itemPos . top ; if ( resizeLeft ) newX = endCorner . left - newW ; if ( resizeTop ) newY = endCorner . top - newH ; let calcSize = this . _calculateGridSize ( newW , newH ) ; const itemSize = this . _resizingItem . getSize ( ) ; const iGridPos = this . _resizingItem . getGridPosition ( ) ; const bottomRightCorner = { col : iGridPos . col + itemSize . x , row : iGridPos . row + itemSize . y , } ; const targetPos = Object . assign ( { } , iGridPos ) ; if ( this . _resizeDirection . includes ( 'top' ) ) targetPos . row = bottomRightCorner . row - calcSize . y ; if ( this . _resizeDirection . includes ( 'left' ) ) targetPos . col = bottomRightCorner . col - calcSize . x ; if ( ! this . _isWithinBoundsX ( targetPos , calcSize ) ) calcSize = this . _fixSizeToBoundsX ( targetPos , calcSize ) ; if ( ! this . _isWithinBoundsY ( targetPos , calcSize ) ) calcSize = this . _fixSizeToBoundsY ( targetPos , calcSize ) ; calcSize = this . _resizingItem . fixResize ( calcSize ) ; if ( calcSize . x != itemSize . x || calcSize . y != itemSize . y ) { this . _resizingItem . setGridPosition ( targetPos , this . _fixToGrid ) ; this . _placeholderRef . instance . setGridPosition ( targetPos ) ; this . _resizingItem . setSize ( calcSize , this . _fixToGrid ) ; this . _placeholderRef . instance . setSize ( calcSize ) ; if ( [ 'up' , 'down' , 'left' , 'right' ] . indexOf ( this . cascade ) >= 0 ) { this . _fixGridCollisions ( targetPos , calcSize ) ; this . _cascadeGrid ( targetPos , calcSize ) ; } } if ( ! this . _fixToGrid ) { this . _resizingItem . setDimensions ( newW , newH ) ; this . _resizingItem . setPosition ( newX , newY ) ; } this . onResize . emit ( this . _resizingItem ) ; this . _resizingItem . onResizeEvent ( ) ; } private _dragStop ( e ) { if ( ! this . isDragging ) return ; this . isDragging = false ; var itemPos = this . _draggingItem . getGridPosition ( ) ; this . _draggingItem . setGridPosition ( itemPos ) ; this . _addToGrid ( this . _draggingItem ) ; this . _cascadeGrid ( ) ; this . _updateSize ( ) ; this . _draggingItem . stopMoving ( ) ; this . _draggingItem . onDragStopEvent ( ) ; this . onDragStop . emit ( this . _draggingItem ) ; this . _cleanDrag ( ) ; this . _placeholderRef . destroy ( ) ; this . _emitOnItemChange ( ) ; if ( this . _zoomOnDrag ) { this . _resetZoom ( ) ; } } private _resizeStop ( e ) { if ( ! this . isResizing ) return ; this . isResizing = false ; const itemDims = this . _resizingItem . getSize ( ) ; this . _resizingItem . setSize ( itemDims ) ; const itemPos = this . _resizingItem . getGridPosition ( ) ; this . _resizingItem . setGridPosition ( itemPos ) ; this . _addToGrid ( this . _resizingItem ) ; this . _cascadeGrid ( ) ; this . _updateSize ( ) ; this . _resizingItem . stopMoving ( ) ; this . _resizingItem . onResizeStopEvent ( ) ; this . onResizeStop . emit ( this . _resizingItem ) ; this . _cleanResize ( ) ; this . _placeholderRef . destroy ( ) ; this . _emitOnItemChange ( ) ; } private _cleanDrag ( ) { this . _draggingItem = null ; this . _posOffset = null ; this . isDragging = false ; this . _dragReady = false ; } private _cleanResize ( ) { this . _resizingItem = null ; this . _resizeDirection = null ; this . isResizing = false ; this . _resizeReady = false ; } private _calculateGridSize ( width , height ) { width += this . marginLeft + this . marginRight ; height += this . marginTop + this . marginBottom ; var sizex = Math . max ( this . minCols , Math . round ( width / ( this . colWidth + this . marginLeft + this . marginRight ) ) ) ; var sizey = Math . max ( this . minRows , Math . round ( height / ( this . rowHeight + this . marginTop + this . marginBottom ) ) ) ; if ( ! this . _isWithinBoundsX ( { col : 1 , row : 1 } , { x : sizex , y : sizey } ) ) sizex = this . _maxCols ; if ( ! this . _isWithinBoundsY ( { col : 1 , row : 1 } , { x : sizex , y : sizey } ) ) sizey = this . _maxRows ; return { 'x' : sizex , 'y' : sizey } ; } private _calculateGridPosition ( left , top ) { var col = Math . max ( 1 , Math . round ( left / ( this . colWidth + this . marginLeft + this . marginRight ) ) + 1 ) ; var row = Math . max ( 1 , Math . round ( top / ( this . rowHeight + this . marginTop + this . marginBottom ) ) + 1 ) ; if ( ! this . _isWithinBoundsX ( { col : col , row : row } , { x : 1 , y : 1 } ) ) col = this . _maxCols ; if ( ! this . _isWithinBoundsY ( { col : col , row : row } , { x : 1 , y : 1 } ) ) row = this . _maxRows ; return { 'col' : col , 'row' : row } ; } private _hasGridCollision ( pos , dims ) { var positions = this . _getCollisions ( pos , dims ) ; if ( positions == null || positions . length == 0 ) return false ; return positions . some ( ( v ) => { return ! ( v === null ) ; } ) ; } private _getCollisions ( pos , dims ) { if ( this . _allowOverlap ) return [ ] ; const returns = [ ] ; if ( ! pos . col ) { pos . col = 1 ; } if ( ! pos . row ) { pos . row = 1 ; } const leftCol = pos . col ; const rightCol = pos . col + dims . x ; const topRow = pos . row ; const bottomRow = pos . row + dims . y ; this . _itemsInGrid . forEach ( ( itemId ) => { const item = this . _items . get ( itemId ) ; if ( ! item ) { this . _itemsInGrid . delete ( itemId ) ; return ; } const itemLeftCol = item . col ; const itemRightCol = item . col + item . sizex ; const itemTopRow = item . row ; const itemBottomRow = item . row + item . sizey ; const withinColumns = leftCol < itemRightCol && itemLeftCol < rightCol ; const withinRows = topRow < itemBottomRow && itemTopRow < bottomRow ; if ( withinColumns && withinRows ) { returns . push ( item ) ; } } ) ; return returns ; } private _fixGridCollisions ( pos , dims ) { const collisions = this . _getCollisions ( pos , dims ) ; if ( collisions . length === 0 ) { return ; } for ( let collision of collisions ) { this . _removeFromGrid ( collision ) ; const itemDims = collision . getSize ( ) ; const itemPos = collision . getGridPosition ( ) ; let newItemPos = { col : itemPos . col , row : itemPos . row } ; if ( this . _collisionFixDirection === 'vertical' ) { newItemPos . row = pos . row + dims . y ; if ( ! this . _isWithinBoundsY ( newItemPos , itemDims ) ) { newItemPos . col = pos . col + dims . x ; newItemPos . row = 1 ; } } else if ( this . _collisionFixDirection === 'horizontal' ) { newItemPos . col = pos . col + dims . x ; if ( ! this . _isWithinBoundsX ( newItemPos , itemDims ) ) { newItemPos . col = 1 ; newItemPos . row = pos . row + dims . y ; } } collision . setGridPosition ( newItemPos ) ; this . _fixGridCollisions ( newItemPos , itemDims ) ; this . _addToGrid ( collision ) ; collision . onCascadeEvent ( ) ; } this . _fixGridCollisions ( pos , dims ) ; } private _cascadeGrid ( pos ? , dims ? ) { if ( this . _destroyed ) return ; if ( this . _allowOverlap ) return ; if ( ! pos !== ! dims ) throw new Error ( 'Cannot cascade with only position and not dimensions' ) ; if ( this . isDragging && this . _draggingItem && ! pos && ! dims ) { pos = this . _draggingItem . getGridPosition ( ) ; dims = this . _draggingItem . getSize ( ) ; } else if ( this . isResizing && this . _resizingItem && ! pos && ! dims ) { pos = this . _resizingItem . getGridPosition ( ) ; dims = this . _resizingItem . getSize ( ) ; } let itemsInGrid = Array . from ( this . _itemsInGrid , ( itemId ) => this . _items . get ( itemId ) ) ; switch ( this . cascade ) { case 'up' : case 'down' : itemsInGrid = itemsInGrid . sort ( NgGridHelper . sortItemsByPositionVertical ) ; const lowestRowPerColumn = new Map < number , number > ( ) ; for ( let item of itemsInGrid ) { if ( item . isFixed ) continue ; const itemDims = item . getSize ( ) ; const itemPos = item . getGridPosition ( ) ; let lowestRowForItem = lowestRowPerColumn . get ( itemPos . col ) || 1 ; for ( let i = 1 ; i < itemDims . x ; i ++ ) { const lowestRowForColumn = lowestRowPerColumn . get ( itemPos . col + i ) || 1 ; lowestRowForItem = Math . max ( lowestRowForColumn , lowestRowForItem ) ; } const leftCol = itemPos . col ; const rightCol = itemPos . col + itemDims . x ; if ( pos && dims ) { const withinColumns = rightCol > pos . col && leftCol < ( pos . col + dims . x ) ; if ( withinColumns ) { const roomAboveItem = itemDims . y <= ( pos . row - lowestRowForItem ) ; if ( ! roomAboveItem ) { lowestRowForItem = Math . max ( lowestRowForItem , pos . row + dims . y ) ; } } } const newPos = { col : itemPos . col , row : lowestRowForItem } ; if ( lowestRowForItem != itemPos . row && this . _isWithinBoundsY ( newPos , itemDims ) ) { this . _removeFromGrid ( item ) ; item . setGridPosition ( newPos ) ; item . onCascadeEvent ( ) ; this . _addToGrid ( item ) ; } for ( let i = 0 ; i < itemDims . x ; i ++ ) { lowestRowPerColumn . set ( itemPos . col + i , lowestRowForItem + itemDims . y ) ; } } break ; case 'left' : case 'right' : itemsInGrid = itemsInGrid . sort ( NgGridHelper . sortItemsByPositionHorizontal ) ; const lowestColumnPerRow = new Map < number , number > ( ) ; for ( let item of itemsInGrid ) { const itemDims = item . getSize ( ) ; const itemPos = item . getGridPosition ( ) ; let lowestColumnForItem = lowestColumnPerRow . get ( itemPos . row ) || 1 ; for ( let i = 1 ; i < itemDims . y ; i ++ ) { let lowestOffsetColumn = lowestColumnPerRow . get ( itemPos . row + i ) || 1 ; lowestColumnForItem = Math . max ( lowestOffsetColumn , lowestColumnForItem ) ; } const topRow = itemPos . row ; const bottomRow = itemPos . row + itemDims . y ; if ( pos && dims ) { const withinRows = bottomRow > pos . col && topRow < ( pos . col + dims . x ) ; if ( withinRows ) { const roomNextToItem = itemDims . x <= ( pos . col - lowestColumnForItem ) ; if ( ! roomNextToItem ) { lowestColumnForItem = Math . max ( lowestColumnForItem , pos . col + dims . x ) ; } } } const newPos = { col : lowestColumnForItem , row : itemPos . row } ; if ( lowestColumnForItem != itemPos . col && this . _isWithinBoundsX ( newPos , itemDims ) ) { this . _removeFromGrid ( item ) ; item . setGridPosition ( newPos ) ; item . onCascadeEvent ( ) ; this . _addToGrid ( item ) ; } for ( let i = 0 ; i < itemDims . y ; i ++ ) { lowestColumnPerRow . set ( itemPos . row + i , lowestColumnForItem + itemDims . x ) ; } } break ; default : break ; } } private _fixGridPosition ( pos , dims ) { if ( ! this . _hasGridCollision ( pos , dims ) ) return pos ; const maxRow = this . _maxRows === 0 ? this . _getMaxRow ( ) : this . _maxRows ; const maxCol = this . _maxCols === 0 ? this . _getMaxCol ( ) : this . _maxCols ; const newPos = { col : pos . col , row : pos . row , } ; if ( this . _itemFixDirection === 'vertical' ) { fixLoop : for ( ; newPos . col <= maxRow ; ) { const itemsInPath = this . _getItemsInVerticalPath ( newPos , dims , newPos . row ) ; let nextRow = newPos . row ; for ( let item of itemsInPath ) { if ( item . row - nextRow >= dims . y ) { newPos . row = nextRow ; break fixLoop ; } nextRow = item . row + item . sizey ; } if ( maxRow - nextRow >= dims . y ) { newPos . row = nextRow ; break fixLoop ; } newPos . col = Math . max ( newPos . col + 1 , Math . min . apply ( Math , itemsInPath . map ( ( item ) => item . col + dims . x ) ) ) ; newPos . row = 1 ; } } else if ( this . _itemFixDirection === 'horizontal' ) { fixLoop : for ( ; newPos . row <= maxRow ; ) { const itemsInPath = this . _getItemsInHorizontalPath ( newPos , dims , newPos . col ) ; let nextCol = newPos . col ; for ( let item of itemsInPath ) { if ( item . col - nextCol >= dims . x ) { newPos . col = nextCol ; break fixLoop ; } nextCol = item . col + item . sizex ; } if ( maxCol - nextCol >= dims . x ) { newPos . col = nextCol ; break fixLoop ; } newPos . row = Math . max ( newPos . row + 1 , Math . min . apply ( Math , itemsInPath . map ( ( item ) => item . row + dims . y ) ) ) ; newPos . col = 1 ; } } return newPos ; } private _getItemsInHorizontalPath ( pos , dims , startColumn = 0 ) { const itemsInPath = [ ] ; const topRow = pos . row + dims . y - 1 ; this . _itemsInGrid . forEach ( ( itemId ) => { const item = this . _items . get ( itemId ) ; if ( item . col + item . sizex - 1 < startColumn ) { return ; } if ( item . row > topRow ) { return ; } if ( item . row + item . sizey - 1 < pos . row ) { return ; } itemsInPath . push ( item ) ; } ) ; return itemsInPath ; } private _getItemsInVerticalPath ( pos , dims , startRow = 0 ) { const itemsInPath = [ ] ; const rightCol = pos . col + dims . x - 1 ; this . _itemsInGrid . forEach ( ( itemId ) => { const item = this . _items . get ( itemId ) ; if ( item . row + item . sizey - 1 < startRow ) { return ; } if ( item . col > rightCol ) { return ; } if ( item . col + item . sizex - 1 < pos . col ) { return ; } itemsInPath . push ( item ) ; } ) ; return itemsInPath ; } private _isWithinBoundsX ( pos , dims , allowExcessiveItems = false ) { return this . _maxCols == 0 || ( allowExcessiveItems && pos . col == 1 ) || ( pos . col + dims . x - 1 ) <= this . _maxCols ; } private _fixPosToBoundsX ( pos , dims ) { if ( ! this . _isWithinBoundsX ( pos , dims ) ) { pos . col = Math . max ( this . _maxCols - ( dims . x - 1 ) , 1 ) ; pos . row ++ ; } return pos ; } private _fixSizeToBoundsX ( pos , dims ) { if ( ! this . _isWithinBoundsX ( pos , dims ) ) { dims . x = Math . max ( this . _maxCols - ( pos . col - 1 ) , 1 ) ; dims . y ++ ; } return dims ; } private _isWithinBoundsY ( pos , dims , allowExcessiveItems = false ) { return this . _maxRows == 0 || ( allowExcessiveItems && pos . row == 1 ) || ( pos . row + dims . y - 1 ) <= this . _maxRows ; } private _fixPosToBoundsY ( pos , dims ) { if ( ! this . _isWithinBoundsY ( pos , dims ) ) { pos . row = Math . max ( this . _maxRows - ( dims . y - 1 ) , 1 ) ; pos . col ++ ; } return pos ; } private _fixSizeToBoundsY ( pos , dims ) { if ( ! this . _isWithinBoundsY ( pos , dims ) ) { dims . y = Math . max ( this . _maxRows - ( pos . row - 1 ) , 1 ) ; dims . x ++ ; } return dims ; } private _isWithinBounds ( pos , dims , allowExcessiveItems = false ) { return this . _isWithinBoundsX ( pos , dims , allowExcessiveItems ) && this . _isWithinBoundsY ( pos , dims , allowExcessiveItems ) ; } private _fixPosToBounds ( pos , dims ) { return this . _fixPosToBoundsX ( this . _fixPosToBoundsY ( pos , dims ) , dims ) ; } private _fixSizeToBounds ( pos , dims ) { return this . _fixSizeToBoundsX ( pos , this . _fixSizeToBoundsY ( pos , dims ) ) ; } private _addToGrid ( item ) { let pos = item . getGridPosition ( ) ; const dims = item . getSize ( ) ; if ( this . _hasGridCollision ( pos , dims ) ) { this . _fixGridCollisions ( pos , dims ) ; pos = item . getGridPosition ( ) ; } if ( this . _allowOverlap ) { item . zIndex = this . _lastZValue ++ ; } this . _itemsInGrid . add ( item . uid ) ; } private _removeFromGrid ( item ) { this . _itemsInGrid . delete ( item . uid ) ; } private _updateSize ( ) { if ( this . _destroyed ) return ; let maxCol = this . _getMaxCol ( ) ; let maxRow = this . _getMaxRow ( ) ; if ( maxCol != this . _curMaxCol || maxRow != this . _curMaxRow ) { this . _curMaxCol = maxCol ; this . _curMaxRow = maxRow ; } this . _renderer . setStyle ( this . _ngEl . nativeElement , 'width' , '100%' ) ; if ( ! this . _elementBasedDynamicRowHeight ) { this . _renderer . setStyle ( this . _ngEl . nativeElement , 'height' , ( maxRow * ( this . rowHeight + this . marginTop + this . marginBottom ) ) + 'px' ) ; } } private _getMaxRow ( ) { const itemsRows = Array . from ( this . _itemsInGrid , ( itemId ) => { const item = this . _items . get ( itemId ) ; if ( ! item ) return 0 ; return item . row + item . sizey - 1 ; } ) ; return Math . max . apply ( null , itemsRows ) ; } private _getMaxCol ( ) { const itemsCols = Array . from ( this . _itemsInGrid , ( itemId ) => { const item = this . _items . get ( itemId ) ; if ( ! item ) return 0 ; return item . col + item . sizex - 1 ; } ) ; return Math . max . apply ( null , itemsCols ) ; } private _getMousePosition ( e ) { if ( ( ( < any > window ) . TouchEvent && e instanceof TouchEvent ) || ( e . touches || e . changedTouches ) ) { e = e . touches . length > 0 ? e . touches [ 0 ] : e . changedTouches [ 0 ] ; } const refPos = this . _ngEl . nativeElement . getBoundingClientRect ( ) ; let left = e . clientX - refPos . left ; let top = e . clientY - refPos . top ; if ( this . cascade == 'down' ) top = refPos . top + refPos . height - e . clientY ; if ( this . cascade == 'right' ) left = refPos . left + refPos . width - e . clientX ; if ( this . isDragging && this . _zoomOnDrag ) { left *= 2 ; top *= 2 ; } return { left : left , top : top } ; } private _getAbsoluteMousePosition ( e ) { if ( ( ( < any > window ) . TouchEvent && e instanceof TouchEvent ) || ( e . touches || e . changedTouches ) ) { e = e . touches . length > 0 ? e . touches [ 0 ] : e . changedTouches [ 0 ] ; } return { left : e . clientX , top : e . clientY } ; } private _getContainerColumns ( ) { const maxWidth = this . _ngEl . nativeElement . getBoundingClientRect ( ) . width ; const itemWidth = this . colWidth + this . marginLeft + this . marginRight ; return Math . floor ( maxWidth / itemWidth ) ; } private _getContainerRows ( ) { const maxHeight = window . innerHeight - this . marginTop - this . marginBottom ; return Math . floor ( maxHeight / ( this . rowHeight + this . marginTop + this . marginBottom ) ) ; } private _getScreenMargin ( ) { const maxWidth = this . _ngEl . nativeElement . getBoundingClientRect ( ) . width ; const itemWidth = this . colWidth + this . marginLeft + this . marginRight ; return Math . floor ( ( maxWidth - ( this . _maxCols * itemWidth ) ) / 2 ) ; ; } private _getItemFromPosition ( position ) { return Array . from ( this . _itemsInGrid , ( itemId ) => this . _items . get ( itemId ) ) . find ( ( item ) => { if ( ! item ) return false ; const size = item . getDimensions ( ) ; const pos = item . getPosition ( ) ; return position . left >= pos . left && position . left < ( pos . left + size . width ) && position . top >= pos . top && position . top < ( pos . top + size . height ) ; } ) ; } private _createPlaceholder ( item ) { const pos = item . getGridPosition ( ) ; const dims = item . getSize ( ) ; const factory = this . componentFactoryResolver . resolveComponentFactory ( NgGridPlaceholder ) ; var componentRef = item . containerRef . createComponent ( factory ) ; this . _placeholderRef = componentRef ; const placeholder = componentRef . instance ; placeholder . registerGrid ( this ) ; placeholder . setCascadeMode ( this . cascade ) ; placeholder . setGridPosition ( { col : pos . col , row : pos . row } ) ; placeholder . setSize ( { x : dims . x , y : dims . y } ) ; } private _emitOnItemChange ( ) { const itemOutput = Array . from ( this . _itemsInGrid ) . map ( ( itemId ) => this . _items . get ( itemId ) ) . filter ( ( item ) => ! ! item ) . map ( ( item ) => item . getEventOutput ( ) ) ; this . onItemChange . emit ( itemOutput ) ; } private _defineListeners ( ) { const element = this . _ngEl . nativeElement ; this . _documentMousemove$ = fromEvent < MouseEvent > ( document , 'mousemove' ) ; this . _documentMouseup$ = fromEvent < MouseEvent > ( document , 'mouseup' ) ; this . _mousedown$ = fromEvent ( element , 'mousedown' ) ; this . _mousemove$ = fromEvent ( element , 'mousemove' ) ; this . _mouseup$ = fromEvent ( element , 'mouseup' ) ; this . _touchstart$ = fromEvent ( element , 'touchstart' ) ; this . _touchmove$ = fromEvent ( element , 'touchmove' ) ; this . _touchend$ = fromEvent ( element , 'touchend' ) ; } private _enableListeners ( ) { if ( this . _enabledListener ) { return ; } this . _enableMouseListeners ( ) ; if ( this . _isTouchDevice ( ) ) { this . _enableTouchListeners ( ) ; } this . _enabledListener = true ; } private _disableListeners ( ) { this . _subscriptions . forEach ( ( subs ) => subs . unsubscribe ( ) ) ; this . _enabledListener = false ; } private _isTouchDevice ( ) { return 'ontouchstart' in window || navigator . maxTouchPoints > 0 ; } ; private _enableTouchListeners ( ) { const touchstartSubs = this . _touchstart$ . subscribe ( ( e ) => this . mouseDownEventHandler ( e ) ) ; const touchmoveSubs = this . _touchmove$ . subscribe ( ( e ) => this . mouseMoveEventHandler ( e ) ) ; const touchendSubs = this . _touchend$ . subscribe ( ( e ) => this . mouseUpEventHandler ( e ) ) ; this . _subscriptions . push ( touchstartSubs , touchmoveSubs , touchendSubs ) ; } private _enableMouseListeners ( ) { const documentMousemoveSubs = this . _documentMousemove$ . subscribe ( ( e ) => this . mouseMoveEventHandler ( e ) ) ; const documentMouseupSubs = this . _documentMouseup$ . subscribe ( ( e ) => this . mouseUpEventHandler ( e ) ) ; const mousedownSubs = this . _mousedown$ . subscribe ( ( e ) => this . mouseDownEventHandler ( e ) ) ; const mousemoveSubs = this . _mousemove$ . subscribe ( ( e ) => this . mouseMoveEventHandler ( e ) ) ; const mouseupSubs = this . _mouseup$ . subscribe ( ( e ) => this . mouseUpEventHandler ( e ) ) ; this . _subscriptions . push ( documentMousemoveSubs , documentMouseupSubs , mousedownSubs , mousemoveSubs , mouseupSubs ) ; } }