298cd642414189d888e2493c7c0d8e7d544bdaab
[sdc/sdc-workflow-designer.git] /
1 /**
2  * @license
3  * Copyright Google Inc. All Rights Reserved.
4  *
5  * Use of this source code is governed by an MIT-style license that can be
6  * found in the LICENSE file at https://angular.io/license
7  */
8 /* tslint:disable:array-type member-access variable-name typedef
9  only-arrow-functions directive-class-suffix component-class-suffix
10  component-selector no-unnecessary-type-assertion arrow-parens*/
11 import {Injectable, Optional, SkipSelf} from '@angular/core';
12 import {ScrollDispatcher} from '../scroll/scroll-dispatcher';
13
14
15 /**
16  * Simple utility for getting the bounds of the browser viewport.
17  * @docs-private
18  */
19 @Injectable()
20 export class ViewportRuler {
21   /** Cached document client rectangle. */
22   private _documentRect?: ClientRect;
23
24   constructor(scrollDispatcher: ScrollDispatcher) {
25     // Subscribe to scroll and resize events and update the document rectangle
26     // on changes.
27         scrollDispatcher.scrolled(0, () => this._cacheViewportGeometry());
28   }
29
30   /** Gets a ClientRect for the viewport's bounds. */
31   getViewportRect(documentRect = this._documentRect): ClientRect {
32     // Cache the document bounding rect so that we don't recompute it for
33     // multiple calls.
34         if (!documentRect) {
35                 this._cacheViewportGeometry();
36                 documentRect = this._documentRect;
37         }
38
39     // Use the document element's bounding rect rather than the window scroll
40     // properties (e.g. pageYOffset, scrollY) due to in issue in Chrome and IE
41     // where window scroll properties and client coordinates
42     // (boundingClientRect, clientX/Y, etc.) are in different conceptual
43     // viewports. Under most circumstances these viewports are equivalent, but
44     // they can disagree when the page is pinch-zoomed (on devices that support
45     // touch). See
46     // https://bugs.chromium.org/p/chromium/issues/detail?id=489206#c4 We use
47     // the documentElement instead of the body because, by default (without a
48     // css reset) browsers typically give the document body an 8px margin, which
49     // is not included in getBoundingClientRect().
50         const scrollPosition = this.getViewportScrollPosition(documentRect);
51         const height = window.innerHeight;
52         const width = window.innerWidth;
53
54         return {
55                 top: scrollPosition.top,
56                 left: scrollPosition.left,
57                 bottom: scrollPosition.top + height,
58                 right: scrollPosition.left + width,
59                 height,
60                 width,
61         };
62   }
63
64
65   /**
66    * Gets the (top, left) scroll position of the viewport.
67    * @param documentRect
68    */
69   getViewportScrollPosition(documentRect = this._documentRect) {
70     // Cache the document bounding rect so that we don't recompute it for
71     // multiple calls.
72         if (!documentRect) {
73                 this._cacheViewportGeometry();
74                 documentRect = this._documentRect;
75         }
76
77     // The top-left-corner of the viewport is determined by the scroll position
78     // of the document body, normally just (scrollLeft, scrollTop). However,
79     // Chrome and Firefox disagree about whether `document.body` or
80     // `document.documentElement` is the scrolled element, so reading
81     // `scrollTop` and `scrollLeft` is inconsistent. However, using the bounding
82     // rect of `document.documentElement` works consistently, where the `top`
83     // and `left` values will equal negative the scroll position.
84         const top = -documentRect.top || document.body.scrollTop ||
85                 window.scrollY || document.documentElement.scrollTop || 0;
86
87         const left = -documentRect.left || document.body.scrollLeft ||
88                 window.scrollX || document.documentElement.scrollLeft || 0;
89
90         return {top, left};
91   }
92
93   /** Caches the latest client rectangle of the document element. */
94   _cacheViewportGeometry() {
95         this._documentRect = document.documentElement.getBoundingClientRect();
96   }
97 }
98
99 export function VIEWPORT_RULER_PROVIDER_FACTORY(
100         parentRuler: ViewportRuler, scrollDispatcher: ScrollDispatcher) {
101   return parentRuler || new ViewportRuler(scrollDispatcher);
102 }
103
104 export const VIEWPORT_RULER_PROVIDER = {
105   // If there is already a ViewportRuler available, use that. Otherwise, provide
106   // a new one.
107   provide: ViewportRuler,
108   deps: [[new Optional(), new SkipSelf(), ViewportRuler], ScrollDispatcher],
109   useFactory: VIEWPORT_RULER_PROVIDER_FACTORY
110 };