source: imaps-frontend/node_modules/resize-observer-polyfill/src/utils/geometry.js@ d565449

main
Last change on this file since d565449 was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 8.1 KB
Line 
1import defineConfigurable from './defineConfigurable.js';
2import getWindowOf from './getWindowOf.js';
3import isBrowser from './isBrowser.js';
4
5// Placeholder of an empty content rectangle.
6const emptyRect = createRectInit(0, 0, 0, 0);
7
8/**
9 * Converts provided string to a number.
10 *
11 * @param {number|string} value
12 * @returns {number}
13 */
14function toFloat(value) {
15 return parseFloat(value) || 0;
16}
17
18/**
19 * Extracts borders size from provided styles.
20 *
21 * @param {CSSStyleDeclaration} styles
22 * @param {...string} positions - Borders positions (top, right, ...)
23 * @returns {number}
24 */
25function getBordersSize(styles, ...positions) {
26 return positions.reduce((size, position) => {
27 const value = styles['border-' + position + '-width'];
28
29 return size + toFloat(value);
30 }, 0);
31}
32
33/**
34 * Extracts paddings sizes from provided styles.
35 *
36 * @param {CSSStyleDeclaration} styles
37 * @returns {Object} Paddings box.
38 */
39function getPaddings(styles) {
40 const positions = ['top', 'right', 'bottom', 'left'];
41 const paddings = {};
42
43 for (const position of positions) {
44 const value = styles['padding-' + position];
45
46 paddings[position] = toFloat(value);
47 }
48
49 return paddings;
50}
51
52/**
53 * Calculates content rectangle of provided SVG element.
54 *
55 * @param {SVGGraphicsElement} target - Element content rectangle of which needs
56 * to be calculated.
57 * @returns {DOMRectInit}
58 */
59function getSVGContentRect(target) {
60 const bbox = target.getBBox();
61
62 return createRectInit(0, 0, bbox.width, bbox.height);
63}
64
65/**
66 * Calculates content rectangle of provided HTMLElement.
67 *
68 * @param {HTMLElement} target - Element for which to calculate the content rectangle.
69 * @returns {DOMRectInit}
70 */
71function getHTMLElementContentRect(target) {
72 // Client width & height properties can't be
73 // used exclusively as they provide rounded values.
74 const {clientWidth, clientHeight} = target;
75
76 // By this condition we can catch all non-replaced inline, hidden and
77 // detached elements. Though elements with width & height properties less
78 // than 0.5 will be discarded as well.
79 //
80 // Without it we would need to implement separate methods for each of
81 // those cases and it's not possible to perform a precise and performance
82 // effective test for hidden elements. E.g. even jQuery's ':visible' filter
83 // gives wrong results for elements with width & height less than 0.5.
84 if (!clientWidth && !clientHeight) {
85 return emptyRect;
86 }
87
88 const styles = getWindowOf(target).getComputedStyle(target);
89 const paddings = getPaddings(styles);
90 const horizPad = paddings.left + paddings.right;
91 const vertPad = paddings.top + paddings.bottom;
92
93 // Computed styles of width & height are being used because they are the
94 // only dimensions available to JS that contain non-rounded values. It could
95 // be possible to utilize the getBoundingClientRect if only it's data wasn't
96 // affected by CSS transformations let alone paddings, borders and scroll bars.
97 let width = toFloat(styles.width),
98 height = toFloat(styles.height);
99
100 // Width & height include paddings and borders when the 'border-box' box
101 // model is applied (except for IE).
102 if (styles.boxSizing === 'border-box') {
103 // Following conditions are required to handle Internet Explorer which
104 // doesn't include paddings and borders to computed CSS dimensions.
105 //
106 // We can say that if CSS dimensions + paddings are equal to the "client"
107 // properties then it's either IE, and thus we don't need to subtract
108 // anything, or an element merely doesn't have paddings/borders styles.
109 if (Math.round(width + horizPad) !== clientWidth) {
110 width -= getBordersSize(styles, 'left', 'right') + horizPad;
111 }
112
113 if (Math.round(height + vertPad) !== clientHeight) {
114 height -= getBordersSize(styles, 'top', 'bottom') + vertPad;
115 }
116 }
117
118 // Following steps can't be applied to the document's root element as its
119 // client[Width/Height] properties represent viewport area of the window.
120 // Besides, it's as well not necessary as the <html> itself neither has
121 // rendered scroll bars nor it can be clipped.
122 if (!isDocumentElement(target)) {
123 // In some browsers (only in Firefox, actually) CSS width & height
124 // include scroll bars size which can be removed at this step as scroll
125 // bars are the only difference between rounded dimensions + paddings
126 // and "client" properties, though that is not always true in Chrome.
127 const vertScrollbar = Math.round(width + horizPad) - clientWidth;
128 const horizScrollbar = Math.round(height + vertPad) - clientHeight;
129
130 // Chrome has a rather weird rounding of "client" properties.
131 // E.g. for an element with content width of 314.2px it sometimes gives
132 // the client width of 315px and for the width of 314.7px it may give
133 // 314px. And it doesn't happen all the time. So just ignore this delta
134 // as a non-relevant.
135 if (Math.abs(vertScrollbar) !== 1) {
136 width -= vertScrollbar;
137 }
138
139 if (Math.abs(horizScrollbar) !== 1) {
140 height -= horizScrollbar;
141 }
142 }
143
144 return createRectInit(paddings.left, paddings.top, width, height);
145}
146
147/**
148 * Checks whether provided element is an instance of the SVGGraphicsElement.
149 *
150 * @param {Element} target - Element to be checked.
151 * @returns {boolean}
152 */
153const isSVGGraphicsElement = (() => {
154 // Some browsers, namely IE and Edge, don't have the SVGGraphicsElement
155 // interface.
156 if (typeof SVGGraphicsElement !== 'undefined') {
157 return target => target instanceof getWindowOf(target).SVGGraphicsElement;
158 }
159
160 // If it's so, then check that element is at least an instance of the
161 // SVGElement and that it has the "getBBox" method.
162 // eslint-disable-next-line no-extra-parens
163 return target => (
164 target instanceof getWindowOf(target).SVGElement &&
165 typeof target.getBBox === 'function'
166 );
167})();
168
169/**
170 * Checks whether provided element is a document element (<html>).
171 *
172 * @param {Element} target - Element to be checked.
173 * @returns {boolean}
174 */
175function isDocumentElement(target) {
176 return target === getWindowOf(target).document.documentElement;
177}
178
179/**
180 * Calculates an appropriate content rectangle for provided html or svg element.
181 *
182 * @param {Element} target - Element content rectangle of which needs to be calculated.
183 * @returns {DOMRectInit}
184 */
185export function getContentRect(target) {
186 if (!isBrowser) {
187 return emptyRect;
188 }
189
190 if (isSVGGraphicsElement(target)) {
191 return getSVGContentRect(target);
192 }
193
194 return getHTMLElementContentRect(target);
195}
196
197/**
198 * Creates rectangle with an interface of the DOMRectReadOnly.
199 * Spec: https://drafts.fxtf.org/geometry/#domrectreadonly
200 *
201 * @param {DOMRectInit} rectInit - Object with rectangle's x/y coordinates and dimensions.
202 * @returns {DOMRectReadOnly}
203 */
204export function createReadOnlyRect({x, y, width, height}) {
205 // If DOMRectReadOnly is available use it as a prototype for the rectangle.
206 const Constr = typeof DOMRectReadOnly !== 'undefined' ? DOMRectReadOnly : Object;
207 const rect = Object.create(Constr.prototype);
208
209 // Rectangle's properties are not writable and non-enumerable.
210 defineConfigurable(rect, {
211 x, y, width, height,
212 top: y,
213 right: x + width,
214 bottom: height + y,
215 left: x
216 });
217
218 return rect;
219}
220
221/**
222 * Creates DOMRectInit object based on the provided dimensions and the x/y coordinates.
223 * Spec: https://drafts.fxtf.org/geometry/#dictdef-domrectinit
224 *
225 * @param {number} x - X coordinate.
226 * @param {number} y - Y coordinate.
227 * @param {number} width - Rectangle's width.
228 * @param {number} height - Rectangle's height.
229 * @returns {DOMRectInit}
230 */
231export function createRectInit(x, y, width, height) {
232 return {x, y, width, height};
233}
Note: See TracBrowser for help on using the repository browser.