blob: 37f0e0d98a7292b1b72681215067cdc434bb8e54 [file] [log] [blame]
Copybara botbe50d492023-11-30 00:16:42 +01001'use strict';
2
3var _getIterator2 = require('babel-runtime/core-js/get-iterator');
4
5var _getIterator3 = _interopRequireDefault(_getIterator2);
6
7var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
8
9var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
10
11var _createClass2 = require('babel-runtime/helpers/createClass');
12
13var _createClass3 = _interopRequireDefault(_createClass2);
14
15var _intervalFunction = require('./interval-function');
16
17var _intervalFunction2 = _interopRequireDefault(_intervalFunction);
18
19function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20
21(function (window, document) {
22 'use strict';
23
24 if (typeof window.ResizeObserver !== 'undefined') {
25 return;
26 }
27
28 document.resizeObservers = [];
29
30 /**
31 * The content rect is defined in section 2.3 ResizeObserverEntry of the spec
32 * @param target the element to calculate the content rect for
33 * @return {{top: (Number|number), left: (Number|number), width: number, height: number}}
34 *
35 * Note:
36 * Avoid using margins on the observed element. The calculation can return incorrect values when margins are involved.
37 *
38 * The following CSS will report incorrect width (Chrome OSX):
39 *
40 * <div id="outer" style="width: 300px; height:300px; background-color: green;overflow:auto;">
41 * <div id="observed" style="width: 400px; height:400px; background-color: yellow; margin:30px; border: 20px solid red; padding:10px;">
42 * </div>
43 * </div>
44 *
45 * The calculated width is 280. The actual (correct) width is 340 since Chrome clips the margin.
46 *
47 * Use an outer container if you really need a "margin":
48 *
49 * <div id="outer" style="width: 300px; height:300px; background-color: green;overflow:auto; padding:30px;">
50 * <div id="observed" style="width: 400px; height:400px; background-color: yellow; margin: 0; border: 20px solid red; padding:10px;">
51 * </div>
52 * </div>
53 *
54 * A more detailed explanation can be fund here:
55 * http://stackoverflow.com/questions/21064101/understanding-offsetwidth-clientwidth-scrollwidth-and-height-respectively
56 */
57 var getContentRect = function getContentRect(target) {
58 var cs = window.getComputedStyle(target);
59 var r = target.getBoundingClientRect();
60 var top = parseFloat(cs.paddingTop) || 0;
61 var left = parseFloat(cs.paddingLeft) || 0;
62 var width = r.width - ((parseFloat(cs.marginLeft) || 0) + (parseFloat(cs.marginRight) || 0) + (parseFloat(cs.borderLeftWidth) || 0) + (parseFloat(cs.borderRightWidth) || 0) + left + (parseFloat(cs.paddingRight) || 0));
63 var height = r.height - ((parseFloat(cs.marginTop) || 0) + (parseFloat(cs.marginBottom) || 0) + (parseFloat(cs.borderTopWidth) || 0) + (parseFloat(cs.borderBottomWidth) || 0) + top + (parseFloat(cs.paddingBottom) || 0));
64 return { width: width, height: height, top: top, left: left };
65 };
66
67 var dimensionHasChanged = function dimensionHasChanged(target, lastWidth, lastHeight) {
68 var _getContentRect = getContentRect(target),
69 width = _getContentRect.width,
70 height = _getContentRect.height;
71
72 return width !== lastWidth || height !== lastHeight;
73 };
74
75 /**
76 * ResizeObservation holds observation information for a single Element.
77 * @param target
78 * @return {{target: *, broadcastWidth, broadcastHeight, isOrphan: (function()), isActive: (function())}}
79 * @constructor
80 */
81 var ResizeObservation = function ResizeObservation(target) {
82 var _getContentRect2 = getContentRect(target),
83 width = _getContentRect2.width,
84 height = _getContentRect2.height;
85
86 return {
87 target: target,
88 broadcastWidth: width,
89 broadcastHeight: height,
90
91 isOrphan: function isOrphan() {
92 return !this.target || !this.target.parentNode;
93 },
94 isActive: function isActive() {
95 return dimensionHasChanged(this.target, this.broadcastWidth, this.broadcastHeight);
96 }
97 };
98 };
99
100 /**
101 * A snapshot of the observed element
102 * @param target
103 * @param rect
104 * @return {{target: *, contentRect: *}}
105 * @constructor
106 */
107 var ResizeObserverEntry = function ResizeObserverEntry(target, rect) {
108 return {
109 target: target,
110 contentRect: rect
111 };
112 };
113
114 /**
115 * The ResizeObserver is used to observe changes to Element's content rect.
116 */
117
118 var ResizeObserver = function () {
119
120 /**
121 * Constructor for instantiating new Resize observers.
122 * @param callback void (sequence<ResizeObserverEntry> entries). The function which will be called on each resize.
123 * @throws {TypeError}
124 */
125 function ResizeObserver(callback) {
126 (0, _classCallCheck3.default)(this, ResizeObserver);
127
128
129 if (typeof callback !== 'function') {
130 throw new TypeError('callback parameter must be a function');
131 }
132
133 this.callback_ = callback;
134 this.observationTargets_ = [];
135 this.activeTargets_ = [];
136
137 document.resizeObservers.push(this);
138 }
139
140 /**
141 * A list of ResizeObservations. It represents all Elements being observed.
142 *
143 * @return {Array}
144 */
145
146
147 (0, _createClass3.default)(ResizeObserver, [{
148 key: 'observe',
149
150
151 /**
152 * Adds target to the list of observed elements.
153 * @param {HTMLElement} target The target to observe
154 */
155 value: function observe(target) {
156 if (target) {
157 if (!(target instanceof HTMLElement)) {
158 throw new TypeError('target parameter must be an HTMLElement');
159 }
160 if (!this.observationTargets_.find(function (t) {
161 return t.target === target;
162 })) {
163 this.observationTargets_.push(ResizeObservation(target));
164 resizeController.start();
165 }
166 }
167 }
168
169 /**
170 * Removes target from the list of observed elements.
171 * @param target The target to remove
172 */
173
174 }, {
175 key: 'unobserve',
176 value: function unobserve(target) {
177 var i = this.observationTargets_.findIndex(function (t) {
178 return t.target === target;
179 });
180 if (i > -1) {
181 this.observationTargets_.splice(i, 1);
182 }
183 }
184
185 /**
186 * Stops the ResizeObserver instance from receiving notifications of resize changes.
187 * Until the observe() method is used again, observer's callback will not be invoked.
188 */
189
190 }, {
191 key: 'disconnect',
192 value: function disconnect() {
193 this.observationTargets_ = [];
194 this.activeTargets_ = [];
195 }
196
197 /**
198 * Removes the ResizeObserver from the list of observers
199 */
200
201 }, {
202 key: 'destroy',
203 value: function destroy() {
204 var _this = this;
205
206 this.disconnect();
207 var i = document.resizeObservers.findIndex(function (o) {
208 return o === _this;
209 });
210 if (i > -1) {
211 document.resizeObservers.splice(i, 1);
212 }
213 }
214 }, {
215 key: 'deleteOrphansAndPopulateActiveTargets_',
216 value: function deleteOrphansAndPopulateActiveTargets_() {
217
218 // Works, but two iterations
219 //this.observationTargets_ = this.observationTargets_.filter( resizeObervation => !resizeObervation.isOrphan());
220 //this.activeTargets_ = this.observationTargets_.filter( resizeObervation => resizeObervation.isActive());
221
222 // Same result as above, one iteration
223 /*
224 this.activeTargets_ = [];
225 let n = this.observationTargets_.length-1;
226 while(n >= 0) {
227 if(this.observationTargets_[n].isOrphan()) {
228 this.observationTargets_.splice(n, 1);
229 }
230 else if(this.observationTargets_[n].isActive()) {
231 this.activeTargets_.push(this.observationTargets_[n]);
232 }
233 n -= 1;
234 }
235 */
236
237 // Same result as above - but reduce is cooler :-)
238 this.activeTargets_ = this.observationTargets_.reduceRight(function (prev, resizeObservation, index, arr) {
239 if (resizeObservation.isOrphan()) {
240 arr.splice(index, 1);
241 } else if (resizeObservation.isActive()) {
242 prev.push(resizeObservation);
243 }
244 return prev;
245 }, []);
246 }
247 }, {
248 key: 'broadcast_',
249 value: function broadcast_() {
250 this.deleteOrphansAndPopulateActiveTargets_();
251 if (this.activeTargets_.length > 0) {
252 var entries = [];
253 var _iteratorNormalCompletion = true;
254 var _didIteratorError = false;
255 var _iteratorError = undefined;
256
257 try {
258 for (var _iterator = (0, _getIterator3.default)(this.activeTargets_), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
259 var resizeObservation = _step.value;
260
261 var rect = getContentRect(resizeObservation.target);
262 resizeObservation.broadcastWidth = rect.width;
263 resizeObservation.broadcastHeight = rect.height;
264 entries.push(ResizeObserverEntry(resizeObservation.target, rect));
265 }
266 } catch (err) {
267 _didIteratorError = true;
268 _iteratorError = err;
269 } finally {
270 try {
271 if (!_iteratorNormalCompletion && _iterator.return) {
272 _iterator.return();
273 }
274 } finally {
275 if (_didIteratorError) {
276 throw _iteratorError;
277 }
278 }
279 }
280
281 this.callback_(entries);
282 this.activeTargets_ = [];
283 }
284 }
285 }, {
286 key: 'observationTargets',
287 get: function get() {
288 return this.observationTargets_;
289 }
290
291 /**
292 * A list of ResizeObservations. It represents all Elements whose size has
293 * changed since last observation broadcast that are eligible for broadcast.
294 *
295 * @return {Array}
296 */
297
298 }, {
299 key: 'activeTargets',
300 get: function get() {
301 return this.activeTargets_;
302 }
303 }]);
304 return ResizeObserver;
305 }();
306
307 //let interval = require('./interval-function');
308
309 /**
310 * Broadcasts Element.resize events
311 * @return {{start: (function()), stop: (function())}}
312 * @constructor
313 */
314
315
316 var ResizeController = function ResizeController() {
317
318 var shouldStop = function shouldStop() {
319 return document.resizeObservers.findIndex(function (resizeObserver) {
320 return resizeObserver.observationTargets.length > 0;
321 }) > -1;
322 };
323
324 var execute = function execute() {
325 //console.log('***** Execute');
326 var _iteratorNormalCompletion2 = true;
327 var _didIteratorError2 = false;
328 var _iteratorError2 = undefined;
329
330 try {
331 for (var _iterator2 = (0, _getIterator3.default)(document.resizeObservers), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
332 var resizeObserver = _step2.value;
333
334 resizeObserver.broadcast_();
335 }
336 } catch (err) {
337 _didIteratorError2 = true;
338 _iteratorError2 = err;
339 } finally {
340 try {
341 if (!_iteratorNormalCompletion2 && _iterator2.return) {
342 _iterator2.return();
343 }
344 } finally {
345 if (_didIteratorError2) {
346 throw _iteratorError2;
347 }
348 }
349 }
350
351 return shouldStop();
352 };
353
354 var interval = (0, _intervalFunction2.default)(200);
355
356 return {
357 start: function start() {
358 if (!interval.started) {
359 //console.log('***** Start poll');
360 interval.start(execute);
361 }
362 }
363 };
364 };
365
366 window.ResizeObserver = ResizeObserver;
367
368 var resizeController = ResizeController();
369 //console.log('***** ResizeObserver ready');
370})(window, document);
371/**
372 * An API for observing changes to Element’s size.
373 *
374 * @See https://wicg.github.io/ResizeObserver/
375 * @ee https://github.com/pelotoncycle/resize-observer
376 *
377 */