Passed
Branch master (0c09a6)
by Stephan
01:45
created

popper.js ➔ ... ➔ getBoundaries   F

Complexity

Conditions 18
Paths 4608

Size

Total Lines 53
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 18
eloc 34
nc 4608
nop 4
dl 0
loc 53
rs 1.2
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like popper.js ➔ ... ➔ getBoundaries often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/**!
2
 * @fileOverview Kickass library to create and place poppers near their reference elements.
3
 * @version 1.15.0
4
 * @license
5
 * Copyright (c) 2016 Federico Zivolo and contributors
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated documentation files (the "Software"), to deal
9
 * in the Software without restriction, including without limitation the rights
10
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
 * copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be included in all
15
 * copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
 * SOFTWARE.
24
 */
25
(function (global, factory) {
26
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
27
	typeof define === 'function' && define.amd ? define(factory) :
0 ignored issues
show
Bug introduced by
The variable define seems to be never declared. If this is a global, consider adding a /** global: define */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
28
	(global.Popper = factory());
29
}(this, (function () { 'use strict';
30
31
var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
32
33
var longerTimeoutBrowsers = ['Edge', 'Trident', 'Firefox'];
34
var timeoutDuration = 0;
35
for (var i = 0; i < longerTimeoutBrowsers.length; i += 1) {
36
  if (isBrowser && navigator.userAgent.indexOf(longerTimeoutBrowsers[i]) >= 0) {
0 ignored issues
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
37
    timeoutDuration = 1;
38
    break;
39
  }
40
}
41
42
function microtaskDebounce(fn) {
43
  var called = false;
44
  return function () {
45
    if (called) {
46
      return;
47
    }
48
    called = true;
49
    window.Promise.resolve().then(function () {
50
      called = false;
51
      fn();
52
    });
53
  };
54
}
55
56
function taskDebounce(fn) {
57
  var scheduled = false;
58
  return function () {
59
    if (!scheduled) {
60
      scheduled = true;
61
      setTimeout(function () {
62
        scheduled = false;
63
        fn();
64
      }, timeoutDuration);
65
    }
66
  };
67
}
68
69
var supportsMicroTasks = isBrowser && window.Promise;
70
71
/**
72
* Create a debounced version of a method, that's asynchronously deferred
73
* but called in the minimum time possible.
74
*
75
* @method
76
* @memberof Popper.Utils
77
* @argument {Function} fn
78
* @returns {Function}
79
*/
80
var debounce = supportsMicroTasks ? microtaskDebounce : taskDebounce;
81
82
/**
83
 * Check if the given variable is a function
84
 * @method
85
 * @memberof Popper.Utils
86
 * @argument {Any} functionToCheck - variable to check
87
 * @returns {Boolean} answer to: is a function?
88
 */
89
function isFunction(functionToCheck) {
90
  var getType = {};
91
  return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
92
}
93
94
/**
95
 * Get CSS computed property of the given element
96
 * @method
97
 * @memberof Popper.Utils
98
 * @argument {Eement} element
99
 * @argument {String} property
100
 */
101
function getStyleComputedProperty(element, property) {
102
  if (element.nodeType !== 1) {
103
    return [];
104
  }
105
  // NOTE: 1 DOM access here
106
  var window = element.ownerDocument.defaultView;
107
  var css = window.getComputedStyle(element, null);
108
  return property ? css[property] : css;
109
}
110
111
/**
112
 * Returns the parentNode or the host of the element
113
 * @method
114
 * @memberof Popper.Utils
115
 * @argument {Element} element
116
 * @returns {Element} parent
117
 */
118
function getParentNode(element) {
119
  if (element.nodeName === 'HTML') {
120
    return element;
121
  }
122
  return element.parentNode || element.host;
123
}
124
125
/**
126
 * Returns the scrolling parent of the given element
127
 * @method
128
 * @memberof Popper.Utils
129
 * @argument {Element} element
130
 * @returns {Element} scroll parent
131
 */
132
function getScrollParent(element) {
133
  // Return body, `getScroll` will take care to get the correct `scrollTop` from it
134
  if (!element) {
135
    return document.body;
136
  }
137
138
  switch (element.nodeName) {
139
    case 'HTML':
140
    case 'BODY':
141
      return element.ownerDocument.body;
142
    case '#document':
143
      return element.body;
144
  }
145
146
  // Firefox want us to check `-x` and `-y` variations as well
147
148
  var _getStyleComputedProp = getStyleComputedProperty(element),
149
      overflow = _getStyleComputedProp.overflow,
150
      overflowX = _getStyleComputedProp.overflowX,
151
      overflowY = _getStyleComputedProp.overflowY;
152
153
  if (/(auto|scroll|overlay)/.test(overflow + overflowY + overflowX)) {
154
    return element;
155
  }
156
157
  return getScrollParent(getParentNode(element));
158
}
159
160
var isIE11 = isBrowser && !!(window.MSInputMethodContext && document.documentMode);
161
var isIE10 = isBrowser && /MSIE 10/.test(navigator.userAgent);
162
163
/**
164
 * Determines if the browser is Internet Explorer
165
 * @method
166
 * @memberof Popper.Utils
167
 * @param {Number} version to check
168
 * @returns {Boolean} isIE
169
 */
170
function isIE(version) {
171
  if (version === 11) {
172
    return isIE11;
173
  }
174
  if (version === 10) {
175
    return isIE10;
176
  }
177
  return isIE11 || isIE10;
178
}
179
180
/**
181
 * Returns the offset parent of the given element
182
 * @method
183
 * @memberof Popper.Utils
184
 * @argument {Element} element
185
 * @returns {Element} offset parent
186
 */
187
function getOffsetParent(element) {
188
  if (!element) {
189
    return document.documentElement;
190
  }
191
192
  var noOffsetParent = isIE(10) ? document.body : null;
193
194
  // NOTE: 1 DOM access here
195
  var offsetParent = element.offsetParent || null;
196
  // Skip hidden elements which don't have an offsetParent
197
  while (offsetParent === noOffsetParent && element.nextElementSibling) {
198
    offsetParent = (element = element.nextElementSibling).offsetParent;
199
  }
200
201
  var nodeName = offsetParent && offsetParent.nodeName;
202
203
  if (!nodeName || nodeName === 'BODY' || nodeName === 'HTML') {
204
    return element ? element.ownerDocument.documentElement : document.documentElement;
205
  }
206
207
  // .offsetParent will return the closest TH, TD or TABLE in case
208
  // no offsetParent is present, I hate this job...
209
  if (['TH', 'TD', 'TABLE'].indexOf(offsetParent.nodeName) !== -1 && getStyleComputedProperty(offsetParent, 'position') === 'static') {
210
    return getOffsetParent(offsetParent);
211
  }
212
213
  return offsetParent;
214
}
215
216
function isOffsetContainer(element) {
217
  var nodeName = element.nodeName;
218
219
  if (nodeName === 'BODY') {
220
    return false;
221
  }
222
  return nodeName === 'HTML' || getOffsetParent(element.firstElementChild) === element;
223
}
224
225
/**
226
 * Finds the root node (document, shadowDOM root) of the given element
227
 * @method
228
 * @memberof Popper.Utils
229
 * @argument {Element} node
230
 * @returns {Element} root node
231
 */
232
function getRoot(node) {
233
  if (node.parentNode !== null) {
234
    return getRoot(node.parentNode);
235
  }
236
237
  return node;
238
}
239
240
/**
241
 * Finds the offset parent common to the two provided nodes
242
 * @method
243
 * @memberof Popper.Utils
244
 * @argument {Element} element1
245
 * @argument {Element} element2
246
 * @returns {Element} common offset parent
247
 */
248
function findCommonOffsetParent(element1, element2) {
249
  // This check is needed to avoid errors in case one of the elements isn't defined for any reason
250
  if (!element1 || !element1.nodeType || !element2 || !element2.nodeType) {
251
    return document.documentElement;
252
  }
253
254
  // Here we make sure to give as "start" the element that comes first in the DOM
255
  var order = element1.compareDocumentPosition(element2) & Node.DOCUMENT_POSITION_FOLLOWING;
0 ignored issues
show
Bug introduced by
The variable Node seems to be never declared. If this is a global, consider adding a /** global: Node */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
256
  var start = order ? element1 : element2;
257
  var end = order ? element2 : element1;
258
259
  // Get common ancestor container
260
  var range = document.createRange();
261
  range.setStart(start, 0);
262
  range.setEnd(end, 0);
263
  var commonAncestorContainer = range.commonAncestorContainer;
264
265
  // Both nodes are inside #document
266
267
  if (element1 !== commonAncestorContainer && element2 !== commonAncestorContainer || start.contains(end)) {
268
    if (isOffsetContainer(commonAncestorContainer)) {
269
      return commonAncestorContainer;
270
    }
271
272
    return getOffsetParent(commonAncestorContainer);
273
  }
274
275
  // one of the nodes is inside shadowDOM, find which one
276
  var element1root = getRoot(element1);
277
  if (element1root.host) {
278
    return findCommonOffsetParent(element1root.host, element2);
279
  } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
280
    return findCommonOffsetParent(element1, getRoot(element2).host);
281
  }
282
}
283
284
/**
285
 * Gets the scroll value of the given element in the given side (top and left)
286
 * @method
287
 * @memberof Popper.Utils
288
 * @argument {Element} element
289
 * @argument {String} side `top` or `left`
290
 * @returns {number} amount of scrolled pixels
291
 */
292
function getScroll(element) {
293
  var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'top';
294
295
  var upperSide = side === 'top' ? 'scrollTop' : 'scrollLeft';
296
  var nodeName = element.nodeName;
297
298
  if (nodeName === 'BODY' || nodeName === 'HTML') {
299
    var html = element.ownerDocument.documentElement;
300
    var scrollingElement = element.ownerDocument.scrollingElement || html;
301
    return scrollingElement[upperSide];
302
  }
303
304
  return element[upperSide];
305
}
306
307
/*
308
 * Sum or subtract the element scroll values (left and top) from a given rect object
309
 * @method
310
 * @memberof Popper.Utils
311
 * @param {Object} rect - Rect object you want to change
312
 * @param {HTMLElement} element - The element from the function reads the scroll values
313
 * @param {Boolean} subtract - set to true if you want to subtract the scroll values
314
 * @return {Object} rect - The modifier rect object
315
 */
316
function includeScroll(rect, element) {
317
  var subtract = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
318
319
  var scrollTop = getScroll(element, 'top');
320
  var scrollLeft = getScroll(element, 'left');
321
  var modifier = subtract ? -1 : 1;
322
  rect.top += scrollTop * modifier;
323
  rect.bottom += scrollTop * modifier;
324
  rect.left += scrollLeft * modifier;
325
  rect.right += scrollLeft * modifier;
326
  return rect;
327
}
328
329
/*
330
 * Helper to detect borders of a given element
331
 * @method
332
 * @memberof Popper.Utils
333
 * @param {CSSStyleDeclaration} styles
334
 * Result of `getStyleComputedProperty` on the given element
335
 * @param {String} axis - `x` or `y`
336
 * @return {number} borders - The borders size of the given axis
337
 */
338
339
function getBordersSize(styles, axis) {
340
  var sideA = axis === 'x' ? 'Left' : 'Top';
341
  var sideB = sideA === 'Left' ? 'Right' : 'Bottom';
342
343
  return parseFloat(styles['border' + sideA + 'Width'], 10) + parseFloat(styles['border' + sideB + 'Width'], 10);
344
}
345
346
function getSize(axis, body, html, computedStyle) {
347
  return Math.max(body['offset' + axis], body['scroll' + axis], html['client' + axis], html['offset' + axis], html['scroll' + axis], isIE(10) ? parseInt(html['offset' + axis]) + parseInt(computedStyle['margin' + (axis === 'Height' ? 'Top' : 'Left')]) + parseInt(computedStyle['margin' + (axis === 'Height' ? 'Bottom' : 'Right')]) : 0);
348
}
349
350
function getWindowSizes(document) {
351
  var body = document.body;
352
  var html = document.documentElement;
353
  var computedStyle = isIE(10) && getComputedStyle(html);
354
355
  return {
356
    height: getSize('Height', body, html, computedStyle),
357
    width: getSize('Width', body, html, computedStyle)
358
  };
359
}
360
361
var classCallCheck = function (instance, Constructor) {
362
  if (!(instance instanceof Constructor)) {
363
    throw new TypeError("Cannot call a class as a function");
364
  }
365
};
366
367
var createClass = function () {
368
  function defineProperties(target, props) {
369
    for (var i = 0; i < props.length; i++) {
370
      var descriptor = props[i];
371
      descriptor.enumerable = descriptor.enumerable || false;
372
      descriptor.configurable = true;
373
      if ("value" in descriptor) descriptor.writable = true;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
374
      Object.defineProperty(target, descriptor.key, descriptor);
375
    }
376
  }
377
378
  return function (Constructor, protoProps, staticProps) {
379
    if (protoProps) defineProperties(Constructor.prototype, protoProps);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
380
    if (staticProps) defineProperties(Constructor, staticProps);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
381
    return Constructor;
382
  };
383
}();
384
385
386
387
388
389
var defineProperty = function (obj, key, value) {
390
  if (key in obj) {
391
    Object.defineProperty(obj, key, {
392
      value: value,
393
      enumerable: true,
394
      configurable: true,
395
      writable: true
396
    });
397
  } else {
398
    obj[key] = value;
399
  }
400
401
  return obj;
402
};
403
404
var _extends = Object.assign || function (target) {
405
  for (var i = 1; i < arguments.length; i++) {
406
    var source = arguments[i];
407
408
    for (var key in source) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
409
      if (Object.prototype.hasOwnProperty.call(source, key)) {
410
        target[key] = source[key];
411
      }
412
    }
413
  }
414
415
  return target;
416
};
417
418
/**
419
 * Given element offsets, generate an output similar to getBoundingClientRect
420
 * @method
421
 * @memberof Popper.Utils
422
 * @argument {Object} offsets
423
 * @returns {Object} ClientRect like output
424
 */
425
function getClientRect(offsets) {
426
  return _extends({}, offsets, {
427
    right: offsets.left + offsets.width,
428
    bottom: offsets.top + offsets.height
429
  });
430
}
431
432
/**
433
 * Get bounding client rect of given element
434
 * @method
435
 * @memberof Popper.Utils
436
 * @param {HTMLElement} element
437
 * @return {Object} client rect
438
 */
439
function getBoundingClientRect(element) {
440
  var rect = {};
441
442
  // IE10 10 FIX: Please, don't ask, the element isn't
443
  // considered in DOM in some circumstances...
444
  // This isn't reproducible in IE10 compatibility mode of IE11
445
  try {
446
    if (isIE(10)) {
447
      rect = element.getBoundingClientRect();
448
      var scrollTop = getScroll(element, 'top');
449
      var scrollLeft = getScroll(element, 'left');
450
      rect.top += scrollTop;
451
      rect.left += scrollLeft;
452
      rect.bottom += scrollTop;
453
      rect.right += scrollLeft;
454
    } else {
455
      rect = element.getBoundingClientRect();
456
    }
457
  } catch (e) {}
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
458
459
  var result = {
460
    left: rect.left,
461
    top: rect.top,
462
    width: rect.right - rect.left,
463
    height: rect.bottom - rect.top
464
  };
465
466
  // subtract scrollbar size from sizes
467
  var sizes = element.nodeName === 'HTML' ? getWindowSizes(element.ownerDocument) : {};
468
  var width = sizes.width || element.clientWidth || result.right - result.left;
469
  var height = sizes.height || element.clientHeight || result.bottom - result.top;
470
471
  var horizScrollbar = element.offsetWidth - width;
472
  var vertScrollbar = element.offsetHeight - height;
473
474
  // if an hypothetical scrollbar is detected, we must be sure it's not a `border`
475
  // we make this check conditional for performance reasons
476
  if (horizScrollbar || vertScrollbar) {
477
    var styles = getStyleComputedProperty(element);
478
    horizScrollbar -= getBordersSize(styles, 'x');
479
    vertScrollbar -= getBordersSize(styles, 'y');
480
481
    result.width -= horizScrollbar;
482
    result.height -= vertScrollbar;
483
  }
484
485
  return getClientRect(result);
486
}
487
488
function getOffsetRectRelativeToArbitraryNode(children, parent) {
489
  var fixedPosition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
490
491
  var isIE10 = isIE(10);
492
  var isHTML = parent.nodeName === 'HTML';
493
  var childrenRect = getBoundingClientRect(children);
494
  var parentRect = getBoundingClientRect(parent);
495
  var scrollParent = getScrollParent(children);
496
497
  var styles = getStyleComputedProperty(parent);
498
  var borderTopWidth = parseFloat(styles.borderTopWidth, 10);
499
  var borderLeftWidth = parseFloat(styles.borderLeftWidth, 10);
500
501
  // In cases where the parent is fixed, we must ignore negative scroll in offset calc
502
  if (fixedPosition && isHTML) {
503
    parentRect.top = Math.max(parentRect.top, 0);
504
    parentRect.left = Math.max(parentRect.left, 0);
505
  }
506
  var offsets = getClientRect({
507
    top: childrenRect.top - parentRect.top - borderTopWidth,
508
    left: childrenRect.left - parentRect.left - borderLeftWidth,
509
    width: childrenRect.width,
510
    height: childrenRect.height
511
  });
512
  offsets.marginTop = 0;
513
  offsets.marginLeft = 0;
514
515
  // Subtract margins of documentElement in case it's being used as parent
516
  // we do this only on HTML because it's the only element that behaves
517
  // differently when margins are applied to it. The margins are included in
518
  // the box of the documentElement, in the other cases not.
519
  if (!isIE10 && isHTML) {
520
    var marginTop = parseFloat(styles.marginTop, 10);
521
    var marginLeft = parseFloat(styles.marginLeft, 10);
522
523
    offsets.top -= borderTopWidth - marginTop;
524
    offsets.bottom -= borderTopWidth - marginTop;
525
    offsets.left -= borderLeftWidth - marginLeft;
526
    offsets.right -= borderLeftWidth - marginLeft;
527
528
    // Attach marginTop and marginLeft because in some circumstances we may need them
529
    offsets.marginTop = marginTop;
530
    offsets.marginLeft = marginLeft;
531
  }
532
533
  if (isIE10 && !fixedPosition ? parent.contains(scrollParent) : parent === scrollParent && scrollParent.nodeName !== 'BODY') {
534
    offsets = includeScroll(offsets, parent);
535
  }
536
537
  return offsets;
538
}
539
540
function getViewportOffsetRectRelativeToArtbitraryNode(element) {
541
  var excludeScroll = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
542
543
  var html = element.ownerDocument.documentElement;
544
  var relativeOffset = getOffsetRectRelativeToArbitraryNode(element, html);
545
  var width = Math.max(html.clientWidth, window.innerWidth || 0);
546
  var height = Math.max(html.clientHeight, window.innerHeight || 0);
547
548
  var scrollTop = !excludeScroll ? getScroll(html) : 0;
549
  var scrollLeft = !excludeScroll ? getScroll(html, 'left') : 0;
550
551
  var offset = {
552
    top: scrollTop - relativeOffset.top + relativeOffset.marginTop,
553
    left: scrollLeft - relativeOffset.left + relativeOffset.marginLeft,
554
    width: width,
555
    height: height
556
  };
557
558
  return getClientRect(offset);
559
}
560
561
/**
562
 * Check if the given element is fixed or is inside a fixed parent
563
 * @method
564
 * @memberof Popper.Utils
565
 * @argument {Element} element
566
 * @argument {Element} customContainer
567
 * @returns {Boolean} answer to "isFixed?"
568
 */
569
function isFixed(element) {
570
  var nodeName = element.nodeName;
571
  if (nodeName === 'BODY' || nodeName === 'HTML') {
572
    return false;
573
  }
574
  if (getStyleComputedProperty(element, 'position') === 'fixed') {
575
    return true;
576
  }
577
  var parentNode = getParentNode(element);
578
  if (!parentNode) {
579
    return false;
580
  }
581
  return isFixed(parentNode);
582
}
583
584
/**
585
 * Finds the first parent of an element that has a transformed property defined
586
 * @method
587
 * @memberof Popper.Utils
588
 * @argument {Element} element
589
 * @returns {Element} first transformed parent or documentElement
590
 */
591
592
function getFixedPositionOffsetParent(element) {
593
  // This check is needed to avoid errors in case one of the elements isn't defined for any reason
594
  if (!element || !element.parentElement || isIE()) {
595
    return document.documentElement;
596
  }
597
  var el = element.parentElement;
598
  while (el && getStyleComputedProperty(el, 'transform') === 'none') {
599
    el = el.parentElement;
600
  }
601
  return el || document.documentElement;
602
}
603
604
/**
605
 * Computed the boundaries limits and return them
606
 * @method
607
 * @memberof Popper.Utils
608
 * @param {HTMLElement} popper
609
 * @param {HTMLElement} reference
610
 * @param {number} padding
611
 * @param {HTMLElement} boundariesElement - Element used to define the boundaries
612
 * @param {Boolean} fixedPosition - Is in fixed position mode
613
 * @returns {Object} Coordinates of the boundaries
614
 */
615
function getBoundaries(popper, reference, padding, boundariesElement) {
616
  var fixedPosition = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
617
618
  // NOTE: 1 DOM access here
619
620
  var boundaries = { top: 0, left: 0 };
621
  var offsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, reference);
622
623
  // Handle viewport case
624
  if (boundariesElement === 'viewport') {
625
    boundaries = getViewportOffsetRectRelativeToArtbitraryNode(offsetParent, fixedPosition);
626
  } else {
627
    // Handle other cases based on DOM element used as boundaries
628
    var boundariesNode = void 0;
0 ignored issues
show
Unused Code introduced by
The assignment to variable boundariesNode seems to be never used. Consider removing it.
Loading history...
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
629
    if (boundariesElement === 'scrollParent') {
630
      boundariesNode = getScrollParent(getParentNode(reference));
631
      if (boundariesNode.nodeName === 'BODY') {
632
        boundariesNode = popper.ownerDocument.documentElement;
633
      }
634
    } else if (boundariesElement === 'window') {
635
      boundariesNode = popper.ownerDocument.documentElement;
636
    } else {
637
      boundariesNode = boundariesElement;
638
    }
639
640
    var offsets = getOffsetRectRelativeToArbitraryNode(boundariesNode, offsetParent, fixedPosition);
641
642
    // In case of HTML, we need a different computation
643
    if (boundariesNode.nodeName === 'HTML' && !isFixed(offsetParent)) {
644
      var _getWindowSizes = getWindowSizes(popper.ownerDocument),
645
          height = _getWindowSizes.height,
646
          width = _getWindowSizes.width;
647
648
      boundaries.top += offsets.top - offsets.marginTop;
649
      boundaries.bottom = height + offsets.top;
650
      boundaries.left += offsets.left - offsets.marginLeft;
651
      boundaries.right = width + offsets.left;
652
    } else {
653
      // for all the other DOM elements, this one is good
654
      boundaries = offsets;
655
    }
656
  }
657
658
  // Add paddings
659
  padding = padding || 0;
660
  var isPaddingNumber = typeof padding === 'number';
661
  boundaries.left += isPaddingNumber ? padding : padding.left || 0;
662
  boundaries.top += isPaddingNumber ? padding : padding.top || 0;
663
  boundaries.right -= isPaddingNumber ? padding : padding.right || 0;
664
  boundaries.bottom -= isPaddingNumber ? padding : padding.bottom || 0;
665
666
  return boundaries;
667
}
668
669
function getArea(_ref) {
670
  var width = _ref.width,
671
      height = _ref.height;
672
673
  return width * height;
674
}
675
676
/**
677
 * Utility used to transform the `auto` placement to the placement with more
678
 * available space.
679
 * @method
680
 * @memberof Popper.Utils
681
 * @argument {Object} data - The data object generated by update method
682
 * @argument {Object} options - Modifiers configuration and options
683
 * @returns {Object} The data object, properly modified
684
 */
685
function computeAutoPlacement(placement, refRect, popper, reference, boundariesElement) {
686
  var padding = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0;
687
688
  if (placement.indexOf('auto') === -1) {
689
    return placement;
690
  }
691
692
  var boundaries = getBoundaries(popper, reference, padding, boundariesElement);
693
694
  var rects = {
695
    top: {
696
      width: boundaries.width,
697
      height: refRect.top - boundaries.top
698
    },
699
    right: {
700
      width: boundaries.right - refRect.right,
701
      height: boundaries.height
702
    },
703
    bottom: {
704
      width: boundaries.width,
705
      height: boundaries.bottom - refRect.bottom
706
    },
707
    left: {
708
      width: refRect.left - boundaries.left,
709
      height: boundaries.height
710
    }
711
  };
712
713
  var sortedAreas = Object.keys(rects).map(function (key) {
714
    return _extends({
715
      key: key
716
    }, rects[key], {
717
      area: getArea(rects[key])
718
    });
719
  }).sort(function (a, b) {
720
    return b.area - a.area;
721
  });
722
723
  var filteredAreas = sortedAreas.filter(function (_ref2) {
724
    var width = _ref2.width,
725
        height = _ref2.height;
726
    return width >= popper.clientWidth && height >= popper.clientHeight;
727
  });
728
729
  var computedPlacement = filteredAreas.length > 0 ? filteredAreas[0].key : sortedAreas[0].key;
730
731
  var variation = placement.split('-')[1];
732
733
  return computedPlacement + (variation ? '-' + variation : '');
734
}
735
736
/**
737
 * Get offsets to the reference element
738
 * @method
739
 * @memberof Popper.Utils
740
 * @param {Object} state
741
 * @param {Element} popper - the popper element
742
 * @param {Element} reference - the reference element (the popper will be relative to this)
743
 * @param {Element} fixedPosition - is in fixed position mode
744
 * @returns {Object} An object containing the offsets which will be applied to the popper
745
 */
746
function getReferenceOffsets(state, popper, reference) {
747
  var fixedPosition = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
748
749
  var commonOffsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, reference);
750
  return getOffsetRectRelativeToArbitraryNode(reference, commonOffsetParent, fixedPosition);
751
}
752
753
/**
754
 * Get the outer sizes of the given element (offset size + margins)
755
 * @method
756
 * @memberof Popper.Utils
757
 * @argument {Element} element
758
 * @returns {Object} object containing width and height properties
759
 */
760
function getOuterSizes(element) {
761
  var window = element.ownerDocument.defaultView;
762
  var styles = window.getComputedStyle(element);
763
  var x = parseFloat(styles.marginTop || 0) + parseFloat(styles.marginBottom || 0);
764
  var y = parseFloat(styles.marginLeft || 0) + parseFloat(styles.marginRight || 0);
765
  var result = {
766
    width: element.offsetWidth + y,
767
    height: element.offsetHeight + x
768
  };
769
  return result;
770
}
771
772
/**
773
 * Get the opposite placement of the given one
774
 * @method
775
 * @memberof Popper.Utils
776
 * @argument {String} placement
777
 * @returns {String} flipped placement
778
 */
779
function getOppositePlacement(placement) {
780
  var hash = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' };
781
  return placement.replace(/left|right|bottom|top/g, function (matched) {
782
    return hash[matched];
783
  });
784
}
785
786
/**
787
 * Get offsets to the popper
788
 * @method
789
 * @memberof Popper.Utils
790
 * @param {Object} position - CSS position the Popper will get applied
791
 * @param {HTMLElement} popper - the popper element
792
 * @param {Object} referenceOffsets - the reference offsets (the popper will be relative to this)
793
 * @param {String} placement - one of the valid placement options
794
 * @returns {Object} popperOffsets - An object containing the offsets which will be applied to the popper
795
 */
796
function getPopperOffsets(popper, referenceOffsets, placement) {
797
  placement = placement.split('-')[0];
798
799
  // Get popper node sizes
800
  var popperRect = getOuterSizes(popper);
801
802
  // Add position, width and height to our offsets object
803
  var popperOffsets = {
804
    width: popperRect.width,
805
    height: popperRect.height
806
  };
807
808
  // depending by the popper placement we have to compute its offsets slightly differently
809
  var isHoriz = ['right', 'left'].indexOf(placement) !== -1;
810
  var mainSide = isHoriz ? 'top' : 'left';
811
  var secondarySide = isHoriz ? 'left' : 'top';
812
  var measurement = isHoriz ? 'height' : 'width';
813
  var secondaryMeasurement = !isHoriz ? 'height' : 'width';
814
815
  popperOffsets[mainSide] = referenceOffsets[mainSide] + referenceOffsets[measurement] / 2 - popperRect[measurement] / 2;
816
  if (placement === secondarySide) {
817
    popperOffsets[secondarySide] = referenceOffsets[secondarySide] - popperRect[secondaryMeasurement];
818
  } else {
819
    popperOffsets[secondarySide] = referenceOffsets[getOppositePlacement(secondarySide)];
820
  }
821
822
  return popperOffsets;
823
}
824
825
/**
826
 * Mimics the `find` method of Array
827
 * @method
828
 * @memberof Popper.Utils
829
 * @argument {Array} arr
830
 * @argument prop
831
 * @argument value
832
 * @returns index or -1
833
 */
834
function find(arr, check) {
835
  // use native find if supported
836
  if (Array.prototype.find) {
837
    return arr.find(check);
838
  }
839
840
  // use `filter` to obtain the same behavior of `find`
841
  return arr.filter(check)[0];
842
}
843
844
/**
845
 * Return the index of the matching object
846
 * @method
847
 * @memberof Popper.Utils
848
 * @argument {Array} arr
849
 * @argument prop
850
 * @argument value
851
 * @returns index or -1
852
 */
853
function findIndex(arr, prop, value) {
854
  // use native findIndex if supported
855
  if (Array.prototype.findIndex) {
856
    return arr.findIndex(function (cur) {
857
      return cur[prop] === value;
858
    });
859
  }
860
861
  // use `find` + `indexOf` if `findIndex` isn't supported
862
  var match = find(arr, function (obj) {
863
    return obj[prop] === value;
864
  });
865
  return arr.indexOf(match);
866
}
867
868
/**
869
 * Loop trough the list of modifiers and run them in order,
870
 * each of them will then edit the data object.
871
 * @method
872
 * @memberof Popper.Utils
873
 * @param {dataObject} data
874
 * @param {Array} modifiers
875
 * @param {String} ends - Optional modifier name used as stopper
876
 * @returns {dataObject}
877
 */
878
function runModifiers(modifiers, data, ends) {
879
  var modifiersToRun = ends === undefined ? modifiers : modifiers.slice(0, findIndex(modifiers, 'name', ends));
880
881
  modifiersToRun.forEach(function (modifier) {
882
    if (modifier['function']) {
883
      // eslint-disable-line dot-notation
884
      console.warn('`modifier.function` is deprecated, use `modifier.fn`!');
885
    }
886
    var fn = modifier['function'] || modifier.fn; // eslint-disable-line dot-notation
887
    if (modifier.enabled && isFunction(fn)) {
888
      // Add properties to offsets to make them a complete clientRect object
889
      // we do this before each modifier to make sure the previous one doesn't
890
      // mess with these values
891
      data.offsets.popper = getClientRect(data.offsets.popper);
892
      data.offsets.reference = getClientRect(data.offsets.reference);
893
894
      data = fn(data, modifier);
895
    }
896
  });
897
898
  return data;
899
}
900
901
/**
902
 * Updates the position of the popper, computing the new offsets and applying
903
 * the new style.<br />
904
 * Prefer `scheduleUpdate` over `update` because of performance reasons.
905
 * @method
906
 * @memberof Popper
907
 */
908
function update() {
909
  // if popper is destroyed, don't perform any further update
910
  if (this.state.isDestroyed) {
911
    return;
912
  }
913
914
  var data = {
915
    instance: this,
916
    styles: {},
917
    arrowStyles: {},
918
    attributes: {},
919
    flipped: false,
920
    offsets: {}
921
  };
922
923
  // compute reference element offsets
924
  data.offsets.reference = getReferenceOffsets(this.state, this.popper, this.reference, this.options.positionFixed);
925
926
  // compute auto placement, store placement inside the data object,
927
  // modifiers will be able to edit `placement` if needed
928
  // and refer to originalPlacement to know the original value
929
  data.placement = computeAutoPlacement(this.options.placement, data.offsets.reference, this.popper, this.reference, this.options.modifiers.flip.boundariesElement, this.options.modifiers.flip.padding);
930
931
  // store the computed placement inside `originalPlacement`
932
  data.originalPlacement = data.placement;
933
934
  data.positionFixed = this.options.positionFixed;
935
936
  // compute the popper offsets
937
  data.offsets.popper = getPopperOffsets(this.popper, data.offsets.reference, data.placement);
938
939
  data.offsets.popper.position = this.options.positionFixed ? 'fixed' : 'absolute';
940
941
  // run the modifiers
942
  data = runModifiers(this.modifiers, data);
943
944
  // the first `update` will call `onCreate` callback
945
  // the other ones will call `onUpdate` callback
946
  if (!this.state.isCreated) {
947
    this.state.isCreated = true;
948
    this.options.onCreate(data);
949
  } else {
950
    this.options.onUpdate(data);
951
  }
952
}
953
954
/**
955
 * Helper used to know if the given modifier is enabled.
956
 * @method
957
 * @memberof Popper.Utils
958
 * @returns {Boolean}
959
 */
960
function isModifierEnabled(modifiers, modifierName) {
961
  return modifiers.some(function (_ref) {
962
    var name = _ref.name,
963
        enabled = _ref.enabled;
964
    return enabled && name === modifierName;
965
  });
966
}
967
968
/**
969
 * Get the prefixed supported property name
970
 * @method
971
 * @memberof Popper.Utils
972
 * @argument {String} property (camelCase)
973
 * @returns {String} prefixed property (camelCase or PascalCase, depending on the vendor prefix)
974
 */
975
function getSupportedPropertyName(property) {
976
  var prefixes = [false, 'ms', 'Webkit', 'Moz', 'O'];
977
  var upperProp = property.charAt(0).toUpperCase() + property.slice(1);
978
979
  for (var i = 0; i < prefixes.length; i++) {
980
    var prefix = prefixes[i];
981
    var toCheck = prefix ? '' + prefix + upperProp : property;
982
    if (typeof document.body.style[toCheck] !== 'undefined') {
983
      return toCheck;
984
    }
985
  }
986
  return null;
987
}
988
989
/**
990
 * Destroys the popper.
991
 * @method
992
 * @memberof Popper
993
 */
994
function destroy() {
995
  this.state.isDestroyed = true;
996
997
  // touch DOM only if `applyStyle` modifier is enabled
998
  if (isModifierEnabled(this.modifiers, 'applyStyle')) {
999
    this.popper.removeAttribute('x-placement');
1000
    this.popper.style.position = '';
1001
    this.popper.style.top = '';
1002
    this.popper.style.left = '';
1003
    this.popper.style.right = '';
1004
    this.popper.style.bottom = '';
1005
    this.popper.style.willChange = '';
1006
    this.popper.style[getSupportedPropertyName('transform')] = '';
1007
  }
1008
1009
  this.disableEventListeners();
1010
1011
  // remove the popper if user explicity asked for the deletion on destroy
1012
  // do not use `remove` because IE11 doesn't support it
1013
  if (this.options.removeOnDestroy) {
1014
    this.popper.parentNode.removeChild(this.popper);
1015
  }
1016
  return this;
1017
}
1018
1019
/**
1020
 * Get the window associated with the element
1021
 * @argument {Element} element
1022
 * @returns {Window}
1023
 */
1024
function getWindow(element) {
1025
  var ownerDocument = element.ownerDocument;
1026
  return ownerDocument ? ownerDocument.defaultView : window;
1027
}
1028
1029
function attachToScrollParents(scrollParent, event, callback, scrollParents) {
1030
  var isBody = scrollParent.nodeName === 'BODY';
1031
  var target = isBody ? scrollParent.ownerDocument.defaultView : scrollParent;
1032
  target.addEventListener(event, callback, { passive: true });
1033
1034
  if (!isBody) {
1035
    attachToScrollParents(getScrollParent(target.parentNode), event, callback, scrollParents);
1036
  }
1037
  scrollParents.push(target);
1038
}
1039
1040
/**
1041
 * Setup needed event listeners used to update the popper position
1042
 * @method
1043
 * @memberof Popper.Utils
1044
 * @private
1045
 */
1046
function setupEventListeners(reference, options, state, updateBound) {
1047
  // Resize event listener on window
1048
  state.updateBound = updateBound;
1049
  getWindow(reference).addEventListener('resize', state.updateBound, { passive: true });
1050
1051
  // Scroll event listener on scroll parents
1052
  var scrollElement = getScrollParent(reference);
1053
  attachToScrollParents(scrollElement, 'scroll', state.updateBound, state.scrollParents);
1054
  state.scrollElement = scrollElement;
1055
  state.eventsEnabled = true;
1056
1057
  return state;
1058
}
1059
1060
/**
1061
 * It will add resize/scroll events and start recalculating
1062
 * position of the popper element when they are triggered.
1063
 * @method
1064
 * @memberof Popper
1065
 */
1066
function enableEventListeners() {
1067
  if (!this.state.eventsEnabled) {
1068
    this.state = setupEventListeners(this.reference, this.options, this.state, this.scheduleUpdate);
1069
  }
1070
}
1071
1072
/**
1073
 * Remove event listeners used to update the popper position
1074
 * @method
1075
 * @memberof Popper.Utils
1076
 * @private
1077
 */
1078
function removeEventListeners(reference, state) {
1079
  // Remove resize event listener on window
1080
  getWindow(reference).removeEventListener('resize', state.updateBound);
1081
1082
  // Remove scroll event listener on scroll parents
1083
  state.scrollParents.forEach(function (target) {
1084
    target.removeEventListener('scroll', state.updateBound);
1085
  });
1086
1087
  // Reset state
1088
  state.updateBound = null;
1089
  state.scrollParents = [];
1090
  state.scrollElement = null;
1091
  state.eventsEnabled = false;
1092
  return state;
1093
}
1094
1095
/**
1096
 * It will remove resize/scroll events and won't recalculate popper position
1097
 * when they are triggered. It also won't trigger `onUpdate` callback anymore,
1098
 * unless you call `update` method manually.
1099
 * @method
1100
 * @memberof Popper
1101
 */
1102
function disableEventListeners() {
1103
  if (this.state.eventsEnabled) {
1104
    cancelAnimationFrame(this.scheduleUpdate);
1105
    this.state = removeEventListeners(this.reference, this.state);
1106
  }
1107
}
1108
1109
/**
1110
 * Tells if a given input is a number
1111
 * @method
1112
 * @memberof Popper.Utils
1113
 * @param {*} input to check
1114
 * @return {Boolean}
1115
 */
1116
function isNumeric(n) {
1117
  return n !== '' && !isNaN(parseFloat(n)) && isFinite(n);
1118
}
1119
1120
/**
1121
 * Set the style to the given popper
1122
 * @method
1123
 * @memberof Popper.Utils
1124
 * @argument {Element} element - Element to apply the style to
1125
 * @argument {Object} styles
1126
 * Object with a list of properties and values which will be applied to the element
1127
 */
1128
function setStyles(element, styles) {
1129
  Object.keys(styles).forEach(function (prop) {
1130
    var unit = '';
1131
    // add unit if the value is numeric and is one of the following
1132
    if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && isNumeric(styles[prop])) {
1133
      unit = 'px';
1134
    }
1135
    element.style[prop] = styles[prop] + unit;
1136
  });
1137
}
1138
1139
/**
1140
 * Set the attributes to the given popper
1141
 * @method
1142
 * @memberof Popper.Utils
1143
 * @argument {Element} element - Element to apply the attributes to
1144
 * @argument {Object} styles
1145
 * Object with a list of properties and values which will be applied to the element
1146
 */
1147
function setAttributes(element, attributes) {
1148
  Object.keys(attributes).forEach(function (prop) {
1149
    var value = attributes[prop];
1150
    if (value !== false) {
1151
      element.setAttribute(prop, attributes[prop]);
1152
    } else {
1153
      element.removeAttribute(prop);
1154
    }
1155
  });
1156
}
1157
1158
/**
1159
 * @function
1160
 * @memberof Modifiers
1161
 * @argument {Object} data - The data object generated by `update` method
1162
 * @argument {Object} data.styles - List of style properties - values to apply to popper element
1163
 * @argument {Object} data.attributes - List of attribute properties - values to apply to popper element
1164
 * @argument {Object} options - Modifiers configuration and options
1165
 * @returns {Object} The same data object
1166
 */
1167
function applyStyle(data) {
1168
  // any property present in `data.styles` will be applied to the popper,
1169
  // in this way we can make the 3rd party modifiers add custom styles to it
1170
  // Be aware, modifiers could override the properties defined in the previous
1171
  // lines of this modifier!
1172
  setStyles(data.instance.popper, data.styles);
1173
1174
  // any property present in `data.attributes` will be applied to the popper,
1175
  // they will be set as HTML attributes of the element
1176
  setAttributes(data.instance.popper, data.attributes);
1177
1178
  // if arrowElement is defined and arrowStyles has some properties
1179
  if (data.arrowElement && Object.keys(data.arrowStyles).length) {
1180
    setStyles(data.arrowElement, data.arrowStyles);
1181
  }
1182
1183
  return data;
1184
}
1185
1186
/**
1187
 * Set the x-placement attribute before everything else because it could be used
1188
 * to add margins to the popper margins needs to be calculated to get the
1189
 * correct popper offsets.
1190
 * @method
1191
 * @memberof Popper.modifiers
1192
 * @param {HTMLElement} reference - The reference element used to position the popper
1193
 * @param {HTMLElement} popper - The HTML element used as popper
1194
 * @param {Object} options - Popper.js options
1195
 */
1196
function applyStyleOnLoad(reference, popper, options, modifierOptions, state) {
1197
  // compute reference element offsets
1198
  var referenceOffsets = getReferenceOffsets(state, popper, reference, options.positionFixed);
1199
1200
  // compute auto placement, store placement inside the data object,
1201
  // modifiers will be able to edit `placement` if needed
1202
  // and refer to originalPlacement to know the original value
1203
  var placement = computeAutoPlacement(options.placement, referenceOffsets, popper, reference, options.modifiers.flip.boundariesElement, options.modifiers.flip.padding);
1204
1205
  popper.setAttribute('x-placement', placement);
1206
1207
  // Apply `position` to popper before anything else because
1208
  // without the position applied we can't guarantee correct computations
1209
  setStyles(popper, { position: options.positionFixed ? 'fixed' : 'absolute' });
1210
1211
  return options;
1212
}
1213
1214
/**
1215
 * @function
1216
 * @memberof Popper.Utils
1217
 * @argument {Object} data - The data object generated by `update` method
1218
 * @argument {Boolean} shouldRound - If the offsets should be rounded at all
1219
 * @returns {Object} The popper's position offsets rounded
1220
 *
1221
 * The tale of pixel-perfect positioning. It's still not 100% perfect, but as
1222
 * good as it can be within reason.
1223
 * Discussion here: https://github.com/FezVrasta/popper.js/pull/715
1224
 *
1225
 * Low DPI screens cause a popper to be blurry if not using full pixels (Safari
1226
 * as well on High DPI screens).
1227
 *
1228
 * Firefox prefers no rounding for positioning and does not have blurriness on
1229
 * high DPI screens.
1230
 *
1231
 * Only horizontal placement and left/right values need to be considered.
1232
 */
1233
function getRoundedOffsets(data, shouldRound) {
1234
  var _data$offsets = data.offsets,
1235
      popper = _data$offsets.popper,
1236
      reference = _data$offsets.reference;
1237
  var round = Math.round,
1238
      floor = Math.floor;
1239
1240
  var noRound = function noRound(v) {
1241
    return v;
1242
  };
1243
1244
  var referenceWidth = round(reference.width);
1245
  var popperWidth = round(popper.width);
1246
1247
  var isVertical = ['left', 'right'].indexOf(data.placement) !== -1;
1248
  var isVariation = data.placement.indexOf('-') !== -1;
1249
  var sameWidthParity = referenceWidth % 2 === popperWidth % 2;
1250
  var bothOddWidth = referenceWidth % 2 === 1 && popperWidth % 2 === 1;
1251
1252
  var horizontalToInteger = !shouldRound ? noRound : isVertical || isVariation || sameWidthParity ? round : floor;
1253
  var verticalToInteger = !shouldRound ? noRound : round;
1254
1255
  return {
1256
    left: horizontalToInteger(bothOddWidth && !isVariation && shouldRound ? popper.left - 1 : popper.left),
1257
    top: verticalToInteger(popper.top),
1258
    bottom: verticalToInteger(popper.bottom),
1259
    right: horizontalToInteger(popper.right)
1260
  };
1261
}
1262
1263
var isFirefox = isBrowser && /Firefox/i.test(navigator.userAgent);
1264
1265
/**
1266
 * @function
1267
 * @memberof Modifiers
1268
 * @argument {Object} data - The data object generated by `update` method
1269
 * @argument {Object} options - Modifiers configuration and options
1270
 * @returns {Object} The data object, properly modified
1271
 */
1272
function computeStyle(data, options) {
1273
  var x = options.x,
1274
      y = options.y;
1275
  var popper = data.offsets.popper;
1276
1277
  // Remove this legacy support in Popper.js v2
1278
1279
  var legacyGpuAccelerationOption = find(data.instance.modifiers, function (modifier) {
1280
    return modifier.name === 'applyStyle';
1281
  }).gpuAcceleration;
1282
  if (legacyGpuAccelerationOption !== undefined) {
1283
    console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!');
1284
  }
1285
  var gpuAcceleration = legacyGpuAccelerationOption !== undefined ? legacyGpuAccelerationOption : options.gpuAcceleration;
1286
1287
  var offsetParent = getOffsetParent(data.instance.popper);
1288
  var offsetParentRect = getBoundingClientRect(offsetParent);
1289
1290
  // Styles
1291
  var styles = {
1292
    position: popper.position
1293
  };
1294
1295
  var offsets = getRoundedOffsets(data, window.devicePixelRatio < 2 || !isFirefox);
1296
1297
  var sideA = x === 'bottom' ? 'top' : 'bottom';
1298
  var sideB = y === 'right' ? 'left' : 'right';
1299
1300
  // if gpuAcceleration is set to `true` and transform is supported,
1301
  //  we use `translate3d` to apply the position to the popper we
1302
  // automatically use the supported prefixed version if needed
1303
  var prefixedProperty = getSupportedPropertyName('transform');
1304
1305
  // now, let's make a step back and look at this code closely (wtf?)
1306
  // If the content of the popper grows once it's been positioned, it
1307
  // may happen that the popper gets misplaced because of the new content
1308
  // overflowing its reference element
1309
  // To avoid this problem, we provide two options (x and y), which allow
1310
  // the consumer to define the offset origin.
1311
  // If we position a popper on top of a reference element, we can set
1312
  // `x` to `top` to make the popper grow towards its top instead of
1313
  // its bottom.
1314
  var left = void 0,
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
1315
      top = void 0;
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
1316
  if (sideA === 'bottom') {
1317
    // when offsetParent is <html> the positioning is relative to the bottom of the screen (excluding the scrollbar)
1318
    // and not the bottom of the html element
1319
    if (offsetParent.nodeName === 'HTML') {
1320
      top = -offsetParent.clientHeight + offsets.bottom;
1321
    } else {
1322
      top = -offsetParentRect.height + offsets.bottom;
1323
    }
1324
  } else {
1325
    top = offsets.top;
1326
  }
1327
  if (sideB === 'right') {
1328
    if (offsetParent.nodeName === 'HTML') {
1329
      left = -offsetParent.clientWidth + offsets.right;
1330
    } else {
1331
      left = -offsetParentRect.width + offsets.right;
1332
    }
1333
  } else {
1334
    left = offsets.left;
1335
  }
1336
  if (gpuAcceleration && prefixedProperty) {
1337
    styles[prefixedProperty] = 'translate3d(' + left + 'px, ' + top + 'px, 0)';
1338
    styles[sideA] = 0;
1339
    styles[sideB] = 0;
1340
    styles.willChange = 'transform';
1341
  } else {
1342
    // othwerise, we use the standard `top`, `left`, `bottom` and `right` properties
1343
    var invertTop = sideA === 'bottom' ? -1 : 1;
1344
    var invertLeft = sideB === 'right' ? -1 : 1;
1345
    styles[sideA] = top * invertTop;
1346
    styles[sideB] = left * invertLeft;
1347
    styles.willChange = sideA + ', ' + sideB;
1348
  }
1349
1350
  // Attributes
1351
  var attributes = {
1352
    'x-placement': data.placement
1353
  };
1354
1355
  // Update `data` attributes, styles and arrowStyles
1356
  data.attributes = _extends({}, attributes, data.attributes);
1357
  data.styles = _extends({}, styles, data.styles);
1358
  data.arrowStyles = _extends({}, data.offsets.arrow, data.arrowStyles);
1359
1360
  return data;
1361
}
1362
1363
/**
1364
 * Helper used to know if the given modifier depends from another one.<br />
1365
 * It checks if the needed modifier is listed and enabled.
1366
 * @method
1367
 * @memberof Popper.Utils
1368
 * @param {Array} modifiers - list of modifiers
1369
 * @param {String} requestingName - name of requesting modifier
1370
 * @param {String} requestedName - name of requested modifier
1371
 * @returns {Boolean}
1372
 */
1373
function isModifierRequired(modifiers, requestingName, requestedName) {
1374
  var requesting = find(modifiers, function (_ref) {
1375
    var name = _ref.name;
1376
    return name === requestingName;
1377
  });
1378
1379
  var isRequired = !!requesting && modifiers.some(function (modifier) {
1380
    return modifier.name === requestedName && modifier.enabled && modifier.order < requesting.order;
1381
  });
1382
1383
  if (!isRequired) {
1384
    var _requesting = '`' + requestingName + '`';
1385
    var requested = '`' + requestedName + '`';
1386
    console.warn(requested + ' modifier is required by ' + _requesting + ' modifier in order to work, be sure to include it before ' + _requesting + '!');
1387
  }
1388
  return isRequired;
1389
}
1390
1391
/**
1392
 * @function
1393
 * @memberof Modifiers
1394
 * @argument {Object} data - The data object generated by update method
1395
 * @argument {Object} options - Modifiers configuration and options
1396
 * @returns {Object} The data object, properly modified
1397
 */
1398
function arrow(data, options) {
1399
  var _data$offsets$arrow;
1400
1401
  // arrow depends on keepTogether in order to work
1402
  if (!isModifierRequired(data.instance.modifiers, 'arrow', 'keepTogether')) {
1403
    return data;
1404
  }
1405
1406
  var arrowElement = options.element;
1407
1408
  // if arrowElement is a string, suppose it's a CSS selector
1409
  if (typeof arrowElement === 'string') {
1410
    arrowElement = data.instance.popper.querySelector(arrowElement);
1411
1412
    // if arrowElement is not found, don't run the modifier
1413
    if (!arrowElement) {
1414
      return data;
1415
    }
1416
  } else {
1417
    // if the arrowElement isn't a query selector we must check that the
1418
    // provided DOM node is child of its popper node
1419
    if (!data.instance.popper.contains(arrowElement)) {
1420
      console.warn('WARNING: `arrow.element` must be child of its popper element!');
1421
      return data;
1422
    }
1423
  }
1424
1425
  var placement = data.placement.split('-')[0];
1426
  var _data$offsets = data.offsets,
1427
      popper = _data$offsets.popper,
1428
      reference = _data$offsets.reference;
1429
1430
  var isVertical = ['left', 'right'].indexOf(placement) !== -1;
1431
1432
  var len = isVertical ? 'height' : 'width';
1433
  var sideCapitalized = isVertical ? 'Top' : 'Left';
1434
  var side = sideCapitalized.toLowerCase();
1435
  var altSide = isVertical ? 'left' : 'top';
1436
  var opSide = isVertical ? 'bottom' : 'right';
1437
  var arrowElementSize = getOuterSizes(arrowElement)[len];
1438
1439
  //
1440
  // extends keepTogether behavior making sure the popper and its
1441
  // reference have enough pixels in conjunction
1442
  //
1443
1444
  // top/left side
1445
  if (reference[opSide] - arrowElementSize < popper[side]) {
1446
    data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowElementSize);
1447
  }
1448
  // bottom/right side
1449
  if (reference[side] + arrowElementSize > popper[opSide]) {
1450
    data.offsets.popper[side] += reference[side] + arrowElementSize - popper[opSide];
1451
  }
1452
  data.offsets.popper = getClientRect(data.offsets.popper);
1453
1454
  // compute center of the popper
1455
  var center = reference[side] + reference[len] / 2 - arrowElementSize / 2;
1456
1457
  // Compute the sideValue using the updated popper offsets
1458
  // take popper margin in account because we don't have this info available
1459
  var css = getStyleComputedProperty(data.instance.popper);
1460
  var popperMarginSide = parseFloat(css['margin' + sideCapitalized], 10);
1461
  var popperBorderSide = parseFloat(css['border' + sideCapitalized + 'Width'], 10);
1462
  var sideValue = center - data.offsets.popper[side] - popperMarginSide - popperBorderSide;
1463
1464
  // prevent arrowElement from being placed not contiguously to its popper
1465
  sideValue = Math.max(Math.min(popper[len] - arrowElementSize, sideValue), 0);
1466
1467
  data.arrowElement = arrowElement;
1468
  data.offsets.arrow = (_data$offsets$arrow = {}, defineProperty(_data$offsets$arrow, side, Math.round(sideValue)), defineProperty(_data$offsets$arrow, altSide, ''), _data$offsets$arrow);
0 ignored issues
show
Comprehensibility introduced by
Usage of the sequence operator is discouraged, since it may lead to obfuscated code.

The sequence or comma operator allows the inclusion of multiple expressions where only is permitted. The result of the sequence is the value of the last expression.

This operator is most often used in for statements.

Used in another places it can make code hard to read, especially when people do not realize it even exists as a seperate operator.

This check looks for usage of the sequence operator in locations where it is not necessary and could be replaced by a series of expressions or statements.

var a,b,c;

a = 1, b = 1,  c= 3;

could just as well be written as:

var a,b,c;

a = 1;
b = 1;
c = 3;

To learn more about the sequence operator, please refer to the MDN.

Loading history...
1469
1470
  return data;
1471
}
1472
1473
/**
1474
 * Get the opposite placement variation of the given one
1475
 * @method
1476
 * @memberof Popper.Utils
1477
 * @argument {String} placement variation
1478
 * @returns {String} flipped placement variation
1479
 */
1480
function getOppositeVariation(variation) {
1481
  if (variation === 'end') {
1482
    return 'start';
1483
  } else if (variation === 'start') {
1484
    return 'end';
1485
  }
1486
  return variation;
1487
}
1488
1489
/**
1490
 * List of accepted placements to use as values of the `placement` option.<br />
1491
 * Valid placements are:
1492
 * - `auto`
1493
 * - `top`
1494
 * - `right`
1495
 * - `bottom`
1496
 * - `left`
1497
 *
1498
 * Each placement can have a variation from this list:
1499
 * - `-start`
1500
 * - `-end`
1501
 *
1502
 * Variations are interpreted easily if you think of them as the left to right
1503
 * written languages. Horizontally (`top` and `bottom`), `start` is left and `end`
1504
 * is right.<br />
1505
 * Vertically (`left` and `right`), `start` is top and `end` is bottom.
1506
 *
1507
 * Some valid examples are:
1508
 * - `top-end` (on top of reference, right aligned)
1509
 * - `right-start` (on right of reference, top aligned)
1510
 * - `bottom` (on bottom, centered)
1511
 * - `auto-end` (on the side with more space available, alignment depends by placement)
1512
 *
1513
 * @static
1514
 * @type {Array}
1515
 * @enum {String}
1516
 * @readonly
1517
 * @method placements
1518
 * @memberof Popper
1519
 */
1520
var placements = ['auto-start', 'auto', 'auto-end', 'top-start', 'top', 'top-end', 'right-start', 'right', 'right-end', 'bottom-end', 'bottom', 'bottom-start', 'left-end', 'left', 'left-start'];
1521
1522
// Get rid of `auto` `auto-start` and `auto-end`
1523
var validPlacements = placements.slice(3);
1524
1525
/**
1526
 * Given an initial placement, returns all the subsequent placements
1527
 * clockwise (or counter-clockwise).
1528
 *
1529
 * @method
1530
 * @memberof Popper.Utils
1531
 * @argument {String} placement - A valid placement (it accepts variations)
1532
 * @argument {Boolean} counter - Set to true to walk the placements counterclockwise
1533
 * @returns {Array} placements including their variations
1534
 */
1535
function clockwise(placement) {
1536
  var counter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
1537
1538
  var index = validPlacements.indexOf(placement);
1539
  var arr = validPlacements.slice(index + 1).concat(validPlacements.slice(0, index));
1540
  return counter ? arr.reverse() : arr;
1541
}
1542
1543
var BEHAVIORS = {
1544
  FLIP: 'flip',
1545
  CLOCKWISE: 'clockwise',
1546
  COUNTERCLOCKWISE: 'counterclockwise'
1547
};
1548
1549
/**
1550
 * @function
1551
 * @memberof Modifiers
1552
 * @argument {Object} data - The data object generated by update method
1553
 * @argument {Object} options - Modifiers configuration and options
1554
 * @returns {Object} The data object, properly modified
1555
 */
1556
function flip(data, options) {
1557
  // if `inner` modifier is enabled, we can't use the `flip` modifier
1558
  if (isModifierEnabled(data.instance.modifiers, 'inner')) {
1559
    return data;
1560
  }
1561
1562
  if (data.flipped && data.placement === data.originalPlacement) {
1563
    // seems like flip is trying to loop, probably there's not enough space on any of the flippable sides
1564
    return data;
1565
  }
1566
1567
  var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, options.boundariesElement, data.positionFixed);
1568
1569
  var placement = data.placement.split('-')[0];
1570
  var placementOpposite = getOppositePlacement(placement);
1571
  var variation = data.placement.split('-')[1] || '';
1572
1573
  var flipOrder = [];
0 ignored issues
show
Unused Code introduced by
The assignment to variable flipOrder seems to be never used. Consider removing it.
Loading history...
1574
1575
  switch (options.behavior) {
1576
    case BEHAVIORS.FLIP:
1577
      flipOrder = [placement, placementOpposite];
1578
      break;
1579
    case BEHAVIORS.CLOCKWISE:
1580
      flipOrder = clockwise(placement);
1581
      break;
1582
    case BEHAVIORS.COUNTERCLOCKWISE:
1583
      flipOrder = clockwise(placement, true);
1584
      break;
1585
    default:
1586
      flipOrder = options.behavior;
1587
  }
1588
1589
  flipOrder.forEach(function (step, index) {
1590
    if (placement !== step || flipOrder.length === index + 1) {
1591
      return data;
1592
    }
1593
1594
    placement = data.placement.split('-')[0];
1595
    placementOpposite = getOppositePlacement(placement);
1596
1597
    var popperOffsets = data.offsets.popper;
1598
    var refOffsets = data.offsets.reference;
1599
1600
    // using floor because the reference offsets may contain decimals we are not going to consider here
1601
    var floor = Math.floor;
1602
    var overlapsRef = placement === 'left' && floor(popperOffsets.right) > floor(refOffsets.left) || placement === 'right' && floor(popperOffsets.left) < floor(refOffsets.right) || placement === 'top' && floor(popperOffsets.bottom) > floor(refOffsets.top) || placement === 'bottom' && floor(popperOffsets.top) < floor(refOffsets.bottom);
1603
1604
    var overflowsLeft = floor(popperOffsets.left) < floor(boundaries.left);
1605
    var overflowsRight = floor(popperOffsets.right) > floor(boundaries.right);
1606
    var overflowsTop = floor(popperOffsets.top) < floor(boundaries.top);
1607
    var overflowsBottom = floor(popperOffsets.bottom) > floor(boundaries.bottom);
1608
1609
    var overflowsBoundaries = placement === 'left' && overflowsLeft || placement === 'right' && overflowsRight || placement === 'top' && overflowsTop || placement === 'bottom' && overflowsBottom;
1610
1611
    // flip the variation if required
1612
    var isVertical = ['top', 'bottom'].indexOf(placement) !== -1;
1613
1614
    // flips variation if reference element overflows boundaries
1615
    var flippedVariationByRef = !!options.flipVariations && (isVertical && variation === 'start' && overflowsLeft || isVertical && variation === 'end' && overflowsRight || !isVertical && variation === 'start' && overflowsTop || !isVertical && variation === 'end' && overflowsBottom);
1616
1617
    // flips variation if popper content overflows boundaries
1618
    var flippedVariationByContent = !!options.flipVariationsByContent && (isVertical && variation === 'start' && overflowsRight || isVertical && variation === 'end' && overflowsLeft || !isVertical && variation === 'start' && overflowsBottom || !isVertical && variation === 'end' && overflowsTop);
1619
1620
    var flippedVariation = flippedVariationByRef || flippedVariationByContent;
1621
1622
    if (overlapsRef || overflowsBoundaries || flippedVariation) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if overlapsRef || overflows...ies || flippedVariation is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
1623
      // this boolean to detect any flip loop
1624
      data.flipped = true;
1625
1626
      if (overlapsRef || overflowsBoundaries) {
1627
        placement = flipOrder[index + 1];
1628
      }
1629
1630
      if (flippedVariation) {
1631
        variation = getOppositeVariation(variation);
1632
      }
1633
1634
      data.placement = placement + (variation ? '-' + variation : '');
1635
1636
      // this object contains `position`, we want to preserve it along with
1637
      // any additional property we may add in the future
1638
      data.offsets.popper = _extends({}, data.offsets.popper, getPopperOffsets(data.instance.popper, data.offsets.reference, data.placement));
1639
1640
      data = runModifiers(data.instance.modifiers, data, 'flip');
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1641
    }
1642
  });
1643
  return data;
1644
}
1645
1646
/**
1647
 * @function
1648
 * @memberof Modifiers
1649
 * @argument {Object} data - The data object generated by update method
1650
 * @argument {Object} options - Modifiers configuration and options
1651
 * @returns {Object} The data object, properly modified
1652
 */
1653
function keepTogether(data) {
1654
  var _data$offsets = data.offsets,
1655
      popper = _data$offsets.popper,
1656
      reference = _data$offsets.reference;
1657
1658
  var placement = data.placement.split('-')[0];
1659
  var floor = Math.floor;
1660
  var isVertical = ['top', 'bottom'].indexOf(placement) !== -1;
1661
  var side = isVertical ? 'right' : 'bottom';
1662
  var opSide = isVertical ? 'left' : 'top';
1663
  var measurement = isVertical ? 'width' : 'height';
1664
1665
  if (popper[side] < floor(reference[opSide])) {
1666
    data.offsets.popper[opSide] = floor(reference[opSide]) - popper[measurement];
1667
  }
1668
  if (popper[opSide] > floor(reference[side])) {
1669
    data.offsets.popper[opSide] = floor(reference[side]);
1670
  }
1671
1672
  return data;
1673
}
1674
1675
/**
1676
 * Converts a string containing value + unit into a px value number
1677
 * @function
1678
 * @memberof {modifiers~offset}
1679
 * @private
1680
 * @argument {String} str - Value + unit string
1681
 * @argument {String} measurement - `height` or `width`
1682
 * @argument {Object} popperOffsets
1683
 * @argument {Object} referenceOffsets
1684
 * @returns {Number|String}
1685
 * Value in pixels, or original string if no values were extracted
1686
 */
1687
function toValue(str, measurement, popperOffsets, referenceOffsets) {
1688
  // separate value from unit
1689
  var split = str.match(/((?:\-|\+)?\d*\.?\d*)(.*)/);
1690
  var value = +split[1];
1691
  var unit = split[2];
1692
1693
  // If it's not a number it's an operator, I guess
1694
  if (!value) {
1695
    return str;
1696
  }
1697
1698
  if (unit.indexOf('%') === 0) {
1699
    var element = void 0;
0 ignored issues
show
Unused Code introduced by
The assignment to variable element seems to be never used. Consider removing it.
Loading history...
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
1700
    switch (unit) {
1701
      case '%p':
1702
        element = popperOffsets;
1703
        break;
1704
      case '%':
1705
      case '%r':
1706
      default:
1707
        element = referenceOffsets;
1708
    }
1709
1710
    var rect = getClientRect(element);
1711
    return rect[measurement] / 100 * value;
1712
  } else if (unit === 'vh' || unit === 'vw') {
1713
    // if is a vh or vw, we calculate the size based on the viewport
1714
    var size = void 0;
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
Unused Code introduced by
The assignment to variable size seems to be never used. Consider removing it.
Loading history...
1715
    if (unit === 'vh') {
1716
      size = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
1717
    } else {
1718
      size = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
1719
    }
1720
    return size / 100 * value;
1721
  } else {
1722
    // if is an explicit pixel unit, we get rid of the unit and keep the value
1723
    // if is an implicit unit, it's px, and we return just the value
1724
    return value;
1725
  }
1726
}
1727
1728
/**
1729
 * Parse an `offset` string to extrapolate `x` and `y` numeric offsets.
1730
 * @function
1731
 * @memberof {modifiers~offset}
1732
 * @private
1733
 * @argument {String} offset
1734
 * @argument {Object} popperOffsets
1735
 * @argument {Object} referenceOffsets
1736
 * @argument {String} basePlacement
1737
 * @returns {Array} a two cells array with x and y offsets in numbers
1738
 */
1739
function parseOffset(offset, popperOffsets, referenceOffsets, basePlacement) {
1740
  var offsets = [0, 0];
1741
1742
  // Use height if placement is left or right and index is 0 otherwise use width
1743
  // in this way the first offset will use an axis and the second one
1744
  // will use the other one
1745
  var useHeight = ['right', 'left'].indexOf(basePlacement) !== -1;
1746
1747
  // Split the offset string to obtain a list of values and operands
1748
  // The regex addresses values with the plus or minus sign in front (+10, -20, etc)
1749
  var fragments = offset.split(/(\+|\-)/).map(function (frag) {
1750
    return frag.trim();
1751
  });
1752
1753
  // Detect if the offset string contains a pair of values or a single one
1754
  // they could be separated by comma or space
1755
  var divider = fragments.indexOf(find(fragments, function (frag) {
1756
    return frag.search(/,|\s/) !== -1;
1757
  }));
1758
1759
  if (fragments[divider] && fragments[divider].indexOf(',') === -1) {
1760
    console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.');
1761
  }
1762
1763
  // If divider is found, we divide the list of values and operands to divide
1764
  // them by ofset X and Y.
1765
  var splitRegex = /\s*,\s*|\s+/;
1766
  var ops = divider !== -1 ? [fragments.slice(0, divider).concat([fragments[divider].split(splitRegex)[0]]), [fragments[divider].split(splitRegex)[1]].concat(fragments.slice(divider + 1))] : [fragments];
1767
1768
  // Convert the values with units to absolute pixels to allow our computations
1769
  ops = ops.map(function (op, index) {
1770
    // Most of the units rely on the orientation of the popper
1771
    var measurement = (index === 1 ? !useHeight : useHeight) ? 'height' : 'width';
1772
    var mergeWithPrevious = false;
1773
    return op
1774
    // This aggregates any `+` or `-` sign that aren't considered operators
1775
    // e.g.: 10 + +5 => [10, +, +5]
1776
    .reduce(function (a, b) {
1777
      if (a[a.length - 1] === '' && ['+', '-'].indexOf(b) !== -1) {
1778
        a[a.length - 1] = b;
1779
        mergeWithPrevious = true;
1780
        return a;
1781
      } else if (mergeWithPrevious) {
1782
        a[a.length - 1] += b;
1783
        mergeWithPrevious = false;
1784
        return a;
1785
      } else {
1786
        return a.concat(b);
1787
      }
1788
    }, [])
1789
    // Here we convert the string values into number values (in px)
1790
    .map(function (str) {
1791
      return toValue(str, measurement, popperOffsets, referenceOffsets);
1792
    });
1793
  });
1794
1795
  // Loop trough the offsets arrays and execute the operations
1796
  ops.forEach(function (op, index) {
1797
    op.forEach(function (frag, index2) {
1798
      if (isNumeric(frag)) {
1799
        offsets[index] += frag * (op[index2 - 1] === '-' ? -1 : 1);
1800
      }
1801
    });
1802
  });
1803
  return offsets;
1804
}
1805
1806
/**
1807
 * @function
1808
 * @memberof Modifiers
1809
 * @argument {Object} data - The data object generated by update method
1810
 * @argument {Object} options - Modifiers configuration and options
1811
 * @argument {Number|String} options.offset=0
1812
 * The offset value as described in the modifier description
1813
 * @returns {Object} The data object, properly modified
1814
 */
1815
function offset(data, _ref) {
1816
  var offset = _ref.offset;
1817
  var placement = data.placement,
1818
      _data$offsets = data.offsets,
1819
      popper = _data$offsets.popper,
1820
      reference = _data$offsets.reference;
1821
1822
  var basePlacement = placement.split('-')[0];
1823
1824
  var offsets = void 0;
0 ignored issues
show
Coding Style introduced by
Consider using undefined instead of void(0). It is equivalent and more straightforward to read.
Loading history...
Unused Code introduced by
The assignment to variable offsets seems to be never used. Consider removing it.
Loading history...
1825
  if (isNumeric(+offset)) {
1826
    offsets = [+offset, 0];
1827
  } else {
1828
    offsets = parseOffset(offset, popper, reference, basePlacement);
1829
  }
1830
1831
  if (basePlacement === 'left') {
1832
    popper.top += offsets[0];
1833
    popper.left -= offsets[1];
1834
  } else if (basePlacement === 'right') {
1835
    popper.top += offsets[0];
1836
    popper.left += offsets[1];
1837
  } else if (basePlacement === 'top') {
1838
    popper.left += offsets[0];
1839
    popper.top -= offsets[1];
1840
  } else if (basePlacement === 'bottom') {
1841
    popper.left += offsets[0];
1842
    popper.top += offsets[1];
1843
  }
1844
1845
  data.popper = popper;
1846
  return data;
1847
}
1848
1849
/**
1850
 * @function
1851
 * @memberof Modifiers
1852
 * @argument {Object} data - The data object generated by `update` method
1853
 * @argument {Object} options - Modifiers configuration and options
1854
 * @returns {Object} The data object, properly modified
1855
 */
1856
function preventOverflow(data, options) {
1857
  var boundariesElement = options.boundariesElement || getOffsetParent(data.instance.popper);
1858
1859
  // If offsetParent is the reference element, we really want to
1860
  // go one step up and use the next offsetParent as reference to
1861
  // avoid to make this modifier completely useless and look like broken
1862
  if (data.instance.reference === boundariesElement) {
1863
    boundariesElement = getOffsetParent(boundariesElement);
1864
  }
1865
1866
  // NOTE: DOM access here
1867
  // resets the popper's position so that the document size can be calculated excluding
1868
  // the size of the popper element itself
1869
  var transformProp = getSupportedPropertyName('transform');
1870
  var popperStyles = data.instance.popper.style; // assignment to help minification
1871
  var top = popperStyles.top,
1872
      left = popperStyles.left,
1873
      transform = popperStyles[transformProp];
1874
1875
  popperStyles.top = '';
1876
  popperStyles.left = '';
1877
  popperStyles[transformProp] = '';
1878
1879
  var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, boundariesElement, data.positionFixed);
1880
1881
  // NOTE: DOM access here
1882
  // restores the original style properties after the offsets have been computed
1883
  popperStyles.top = top;
1884
  popperStyles.left = left;
1885
  popperStyles[transformProp] = transform;
1886
1887
  options.boundaries = boundaries;
1888
1889
  var order = options.priority;
1890
  var popper = data.offsets.popper;
1891
1892
  var check = {
1893
    primary: function primary(placement) {
1894
      var value = popper[placement];
1895
      if (popper[placement] < boundaries[placement] && !options.escapeWithReference) {
1896
        value = Math.max(popper[placement], boundaries[placement]);
1897
      }
1898
      return defineProperty({}, placement, value);
1899
    },
1900
    secondary: function secondary(placement) {
1901
      var mainSide = placement === 'right' ? 'left' : 'top';
1902
      var value = popper[mainSide];
1903
      if (popper[placement] > boundaries[placement] && !options.escapeWithReference) {
1904
        value = Math.min(popper[mainSide], boundaries[placement] - (placement === 'right' ? popper.width : popper.height));
1905
      }
1906
      return defineProperty({}, mainSide, value);
1907
    }
1908
  };
1909
1910
  order.forEach(function (placement) {
1911
    var side = ['left', 'top'].indexOf(placement) !== -1 ? 'primary' : 'secondary';
1912
    popper = _extends({}, popper, check[side](placement));
1913
  });
1914
1915
  data.offsets.popper = popper;
1916
1917
  return data;
1918
}
1919
1920
/**
1921
 * @function
1922
 * @memberof Modifiers
1923
 * @argument {Object} data - The data object generated by `update` method
1924
 * @argument {Object} options - Modifiers configuration and options
1925
 * @returns {Object} The data object, properly modified
1926
 */
1927
function shift(data) {
1928
  var placement = data.placement;
1929
  var basePlacement = placement.split('-')[0];
1930
  var shiftvariation = placement.split('-')[1];
1931
1932
  // if shift shiftvariation is specified, run the modifier
1933
  if (shiftvariation) {
1934
    var _data$offsets = data.offsets,
1935
        reference = _data$offsets.reference,
1936
        popper = _data$offsets.popper;
1937
1938
    var isVertical = ['bottom', 'top'].indexOf(basePlacement) !== -1;
1939
    var side = isVertical ? 'left' : 'top';
1940
    var measurement = isVertical ? 'width' : 'height';
1941
1942
    var shiftOffsets = {
1943
      start: defineProperty({}, side, reference[side]),
1944
      end: defineProperty({}, side, reference[side] + reference[measurement] - popper[measurement])
1945
    };
1946
1947
    data.offsets.popper = _extends({}, popper, shiftOffsets[shiftvariation]);
1948
  }
1949
1950
  return data;
1951
}
1952
1953
/**
1954
 * @function
1955
 * @memberof Modifiers
1956
 * @argument {Object} data - The data object generated by update method
1957
 * @argument {Object} options - Modifiers configuration and options
1958
 * @returns {Object} The data object, properly modified
1959
 */
1960
function hide(data) {
1961
  if (!isModifierRequired(data.instance.modifiers, 'hide', 'preventOverflow')) {
1962
    return data;
1963
  }
1964
1965
  var refRect = data.offsets.reference;
1966
  var bound = find(data.instance.modifiers, function (modifier) {
1967
    return modifier.name === 'preventOverflow';
1968
  }).boundaries;
1969
1970
  if (refRect.bottom < bound.top || refRect.left > bound.right || refRect.top > bound.bottom || refRect.right < bound.left) {
1971
    // Avoid unnecessary DOM access if visibility hasn't changed
1972
    if (data.hide === true) {
1973
      return data;
1974
    }
1975
1976
    data.hide = true;
1977
    data.attributes['x-out-of-boundaries'] = '';
1978
  } else {
1979
    // Avoid unnecessary DOM access if visibility hasn't changed
1980
    if (data.hide === false) {
1981
      return data;
1982
    }
1983
1984
    data.hide = false;
1985
    data.attributes['x-out-of-boundaries'] = false;
1986
  }
1987
1988
  return data;
1989
}
1990
1991
/**
1992
 * @function
1993
 * @memberof Modifiers
1994
 * @argument {Object} data - The data object generated by `update` method
1995
 * @argument {Object} options - Modifiers configuration and options
1996
 * @returns {Object} The data object, properly modified
1997
 */
1998
function inner(data) {
1999
  var placement = data.placement;
2000
  var basePlacement = placement.split('-')[0];
2001
  var _data$offsets = data.offsets,
2002
      popper = _data$offsets.popper,
2003
      reference = _data$offsets.reference;
2004
2005
  var isHoriz = ['left', 'right'].indexOf(basePlacement) !== -1;
2006
2007
  var subtractLength = ['top', 'left'].indexOf(basePlacement) === -1;
2008
2009
  popper[isHoriz ? 'left' : 'top'] = reference[basePlacement] - (subtractLength ? popper[isHoriz ? 'width' : 'height'] : 0);
2010
2011
  data.placement = getOppositePlacement(placement);
2012
  data.offsets.popper = getClientRect(popper);
2013
2014
  return data;
2015
}
2016
2017
/**
2018
 * Modifier function, each modifier can have a function of this type assigned
2019
 * to its `fn` property.<br />
2020
 * These functions will be called on each update, this means that you must
2021
 * make sure they are performant enough to avoid performance bottlenecks.
2022
 *
2023
 * @function ModifierFn
2024
 * @argument {dataObject} data - The data object generated by `update` method
2025
 * @argument {Object} options - Modifiers configuration and options
2026
 * @returns {dataObject} The data object, properly modified
2027
 */
2028
2029
/**
2030
 * Modifiers are plugins used to alter the behavior of your poppers.<br />
2031
 * Popper.js uses a set of 9 modifiers to provide all the basic functionalities
2032
 * needed by the library.
2033
 *
2034
 * Usually you don't want to override the `order`, `fn` and `onLoad` props.
2035
 * All the other properties are configurations that could be tweaked.
2036
 * @namespace modifiers
2037
 */
2038
var modifiers = {
2039
  /**
2040
   * Modifier used to shift the popper on the start or end of its reference
2041
   * element.<br />
2042
   * It will read the variation of the `placement` property.<br />
2043
   * It can be one either `-end` or `-start`.
2044
   * @memberof modifiers
2045
   * @inner
2046
   */
2047
  shift: {
2048
    /** @prop {number} order=100 - Index used to define the order of execution */
2049
    order: 100,
2050
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
2051
    enabled: true,
2052
    /** @prop {ModifierFn} */
2053
    fn: shift
2054
  },
2055
2056
  /**
2057
   * The `offset` modifier can shift your popper on both its axis.
2058
   *
2059
   * It accepts the following units:
2060
   * - `px` or unit-less, interpreted as pixels
2061
   * - `%` or `%r`, percentage relative to the length of the reference element
2062
   * - `%p`, percentage relative to the length of the popper element
2063
   * - `vw`, CSS viewport width unit
2064
   * - `vh`, CSS viewport height unit
2065
   *
2066
   * For length is intended the main axis relative to the placement of the popper.<br />
2067
   * This means that if the placement is `top` or `bottom`, the length will be the
2068
   * `width`. In case of `left` or `right`, it will be the `height`.
2069
   *
2070
   * You can provide a single value (as `Number` or `String`), or a pair of values
2071
   * as `String` divided by a comma or one (or more) white spaces.<br />
2072
   * The latter is a deprecated method because it leads to confusion and will be
2073
   * removed in v2.<br />
2074
   * Additionally, it accepts additions and subtractions between different units.
2075
   * Note that multiplications and divisions aren't supported.
2076
   *
2077
   * Valid examples are:
2078
   * ```
2079
   * 10
2080
   * '10%'
2081
   * '10, 10'
2082
   * '10%, 10'
2083
   * '10 + 10%'
2084
   * '10 - 5vh + 3%'
2085
   * '-10px + 5vh, 5px - 6%'
2086
   * ```
2087
   * > **NB**: If you desire to apply offsets to your poppers in a way that may make them overlap
2088
   * > with their reference element, unfortunately, you will have to disable the `flip` modifier.
2089
   * > You can read more on this at this [issue](https://github.com/FezVrasta/popper.js/issues/373).
2090
   *
2091
   * @memberof modifiers
2092
   * @inner
2093
   */
2094
  offset: {
2095
    /** @prop {number} order=200 - Index used to define the order of execution */
2096
    order: 200,
2097
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
2098
    enabled: true,
2099
    /** @prop {ModifierFn} */
2100
    fn: offset,
2101
    /** @prop {Number|String} offset=0
2102
     * The offset value as described in the modifier description
2103
     */
2104
    offset: 0
2105
  },
2106
2107
  /**
2108
   * Modifier used to prevent the popper from being positioned outside the boundary.
2109
   *
2110
   * A scenario exists where the reference itself is not within the boundaries.<br />
2111
   * We can say it has "escaped the boundaries" — or just "escaped".<br />
2112
   * In this case we need to decide whether the popper should either:
2113
   *
2114
   * - detach from the reference and remain "trapped" in the boundaries, or
2115
   * - if it should ignore the boundary and "escape with its reference"
2116
   *
2117
   * When `escapeWithReference` is set to`true` and reference is completely
2118
   * outside its boundaries, the popper will overflow (or completely leave)
2119
   * the boundaries in order to remain attached to the edge of the reference.
2120
   *
2121
   * @memberof modifiers
2122
   * @inner
2123
   */
2124
  preventOverflow: {
2125
    /** @prop {number} order=300 - Index used to define the order of execution */
2126
    order: 300,
2127
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
2128
    enabled: true,
2129
    /** @prop {ModifierFn} */
2130
    fn: preventOverflow,
2131
    /**
2132
     * @prop {Array} [priority=['left','right','top','bottom']]
2133
     * Popper will try to prevent overflow following these priorities by default,
2134
     * then, it could overflow on the left and on top of the `boundariesElement`
2135
     */
2136
    priority: ['left', 'right', 'top', 'bottom'],
2137
    /**
2138
     * @prop {number} padding=5
2139
     * Amount of pixel used to define a minimum distance between the boundaries
2140
     * and the popper. This makes sure the popper always has a little padding
2141
     * between the edges of its container
2142
     */
2143
    padding: 5,
2144
    /**
2145
     * @prop {String|HTMLElement} boundariesElement='scrollParent'
2146
     * Boundaries used by the modifier. Can be `scrollParent`, `window`,
2147
     * `viewport` or any DOM element.
2148
     */
2149
    boundariesElement: 'scrollParent'
2150
  },
2151
2152
  /**
2153
   * Modifier used to make sure the reference and its popper stay near each other
2154
   * without leaving any gap between the two. Especially useful when the arrow is
2155
   * enabled and you want to ensure that it points to its reference element.
2156
   * It cares only about the first axis. You can still have poppers with margin
2157
   * between the popper and its reference element.
2158
   * @memberof modifiers
2159
   * @inner
2160
   */
2161
  keepTogether: {
2162
    /** @prop {number} order=400 - Index used to define the order of execution */
2163
    order: 400,
2164
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
2165
    enabled: true,
2166
    /** @prop {ModifierFn} */
2167
    fn: keepTogether
2168
  },
2169
2170
  /**
2171
   * This modifier is used to move the `arrowElement` of the popper to make
2172
   * sure it is positioned between the reference element and its popper element.
2173
   * It will read the outer size of the `arrowElement` node to detect how many
2174
   * pixels of conjunction are needed.
2175
   *
2176
   * It has no effect if no `arrowElement` is provided.
2177
   * @memberof modifiers
2178
   * @inner
2179
   */
2180
  arrow: {
2181
    /** @prop {number} order=500 - Index used to define the order of execution */
2182
    order: 500,
2183
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
2184
    enabled: true,
2185
    /** @prop {ModifierFn} */
2186
    fn: arrow,
2187
    /** @prop {String|HTMLElement} element='[x-arrow]' - Selector or node used as arrow */
2188
    element: '[x-arrow]'
2189
  },
2190
2191
  /**
2192
   * Modifier used to flip the popper's placement when it starts to overlap its
2193
   * reference element.
2194
   *
2195
   * Requires the `preventOverflow` modifier before it in order to work.
2196
   *
2197
   * **NOTE:** this modifier will interrupt the current update cycle and will
2198
   * restart it if it detects the need to flip the placement.
2199
   * @memberof modifiers
2200
   * @inner
2201
   */
2202
  flip: {
2203
    /** @prop {number} order=600 - Index used to define the order of execution */
2204
    order: 600,
2205
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
2206
    enabled: true,
2207
    /** @prop {ModifierFn} */
2208
    fn: flip,
2209
    /**
2210
     * @prop {String|Array} behavior='flip'
2211
     * The behavior used to change the popper's placement. It can be one of
2212
     * `flip`, `clockwise`, `counterclockwise` or an array with a list of valid
2213
     * placements (with optional variations)
2214
     */
2215
    behavior: 'flip',
2216
    /**
2217
     * @prop {number} padding=5
2218
     * The popper will flip if it hits the edges of the `boundariesElement`
2219
     */
2220
    padding: 5,
2221
    /**
2222
     * @prop {String|HTMLElement} boundariesElement='viewport'
2223
     * The element which will define the boundaries of the popper position.
2224
     * The popper will never be placed outside of the defined boundaries
2225
     * (except if `keepTogether` is enabled)
2226
     */
2227
    boundariesElement: 'viewport',
2228
    /**
2229
     * @prop {Boolean} flipVariations=false
2230
     * The popper will switch placement variation between `-start` and `-end` when
2231
     * the reference element overlaps its boundaries.
2232
     *
2233
     * The original placement should have a set variation.
2234
     */
2235
    flipVariations: false,
2236
    /**
2237
     * @prop {Boolean} flipVariationsByContent=false
2238
     * The popper will switch placement variation between `-start` and `-end` when
2239
     * the popper element overlaps its reference boundaries.
2240
     *
2241
     * The original placement should have a set variation.
2242
     */
2243
    flipVariationsByContent: false
2244
  },
2245
2246
  /**
2247
   * Modifier used to make the popper flow toward the inner of the reference element.
2248
   * By default, when this modifier is disabled, the popper will be placed outside
2249
   * the reference element.
2250
   * @memberof modifiers
2251
   * @inner
2252
   */
2253
  inner: {
2254
    /** @prop {number} order=700 - Index used to define the order of execution */
2255
    order: 700,
2256
    /** @prop {Boolean} enabled=false - Whether the modifier is enabled or not */
2257
    enabled: false,
2258
    /** @prop {ModifierFn} */
2259
    fn: inner
2260
  },
2261
2262
  /**
2263
   * Modifier used to hide the popper when its reference element is outside of the
2264
   * popper boundaries. It will set a `x-out-of-boundaries` attribute which can
2265
   * be used to hide with a CSS selector the popper when its reference is
2266
   * out of boundaries.
2267
   *
2268
   * Requires the `preventOverflow` modifier before it in order to work.
2269
   * @memberof modifiers
2270
   * @inner
2271
   */
2272
  hide: {
2273
    /** @prop {number} order=800 - Index used to define the order of execution */
2274
    order: 800,
2275
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
2276
    enabled: true,
2277
    /** @prop {ModifierFn} */
2278
    fn: hide
2279
  },
2280
2281
  /**
2282
   * Computes the style that will be applied to the popper element to gets
2283
   * properly positioned.
2284
   *
2285
   * Note that this modifier will not touch the DOM, it just prepares the styles
2286
   * so that `applyStyle` modifier can apply it. This separation is useful
2287
   * in case you need to replace `applyStyle` with a custom implementation.
2288
   *
2289
   * This modifier has `850` as `order` value to maintain backward compatibility
2290
   * with previous versions of Popper.js. Expect the modifiers ordering method
2291
   * to change in future major versions of the library.
2292
   *
2293
   * @memberof modifiers
2294
   * @inner
2295
   */
2296
  computeStyle: {
2297
    /** @prop {number} order=850 - Index used to define the order of execution */
2298
    order: 850,
2299
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
2300
    enabled: true,
2301
    /** @prop {ModifierFn} */
2302
    fn: computeStyle,
2303
    /**
2304
     * @prop {Boolean} gpuAcceleration=true
2305
     * If true, it uses the CSS 3D transformation to position the popper.
2306
     * Otherwise, it will use the `top` and `left` properties
2307
     */
2308
    gpuAcceleration: true,
2309
    /**
2310
     * @prop {string} [x='bottom']
2311
     * Where to anchor the X axis (`bottom` or `top`). AKA X offset origin.
2312
     * Change this if your popper should grow in a direction different from `bottom`
2313
     */
2314
    x: 'bottom',
2315
    /**
2316
     * @prop {string} [x='left']
2317
     * Where to anchor the Y axis (`left` or `right`). AKA Y offset origin.
2318
     * Change this if your popper should grow in a direction different from `right`
2319
     */
2320
    y: 'right'
2321
  },
2322
2323
  /**
2324
   * Applies the computed styles to the popper element.
2325
   *
2326
   * All the DOM manipulations are limited to this modifier. This is useful in case
2327
   * you want to integrate Popper.js inside a framework or view library and you
2328
   * want to delegate all the DOM manipulations to it.
2329
   *
2330
   * Note that if you disable this modifier, you must make sure the popper element
2331
   * has its position set to `absolute` before Popper.js can do its work!
2332
   *
2333
   * Just disable this modifier and define your own to achieve the desired effect.
2334
   *
2335
   * @memberof modifiers
2336
   * @inner
2337
   */
2338
  applyStyle: {
2339
    /** @prop {number} order=900 - Index used to define the order of execution */
2340
    order: 900,
2341
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
2342
    enabled: true,
2343
    /** @prop {ModifierFn} */
2344
    fn: applyStyle,
2345
    /** @prop {Function} */
2346
    onLoad: applyStyleOnLoad,
2347
    /**
2348
     * @deprecated since version 1.10.0, the property moved to `computeStyle` modifier
2349
     * @prop {Boolean} gpuAcceleration=true
2350
     * If true, it uses the CSS 3D transformation to position the popper.
2351
     * Otherwise, it will use the `top` and `left` properties
2352
     */
2353
    gpuAcceleration: undefined
2354
  }
2355
};
2356
2357
/**
2358
 * The `dataObject` is an object containing all the information used by Popper.js.
2359
 * This object is passed to modifiers and to the `onCreate` and `onUpdate` callbacks.
2360
 * @name dataObject
2361
 * @property {Object} data.instance The Popper.js instance
2362
 * @property {String} data.placement Placement applied to popper
2363
 * @property {String} data.originalPlacement Placement originally defined on init
2364
 * @property {Boolean} data.flipped True if popper has been flipped by flip modifier
2365
 * @property {Boolean} data.hide True if the reference element is out of boundaries, useful to know when to hide the popper
2366
 * @property {HTMLElement} data.arrowElement Node used as arrow by arrow modifier
2367
 * @property {Object} data.styles Any CSS property defined here will be applied to the popper. It expects the JavaScript nomenclature (eg. `marginBottom`)
2368
 * @property {Object} data.arrowStyles Any CSS property defined here will be applied to the popper arrow. It expects the JavaScript nomenclature (eg. `marginBottom`)
2369
 * @property {Object} data.boundaries Offsets of the popper boundaries
2370
 * @property {Object} data.offsets The measurements of popper, reference and arrow elements
2371
 * @property {Object} data.offsets.popper `top`, `left`, `width`, `height` values
2372
 * @property {Object} data.offsets.reference `top`, `left`, `width`, `height` values
2373
 * @property {Object} data.offsets.arrow] `top` and `left` offsets, only one of them will be different from 0
2374
 */
2375
2376
/**
2377
 * Default options provided to Popper.js constructor.<br />
2378
 * These can be overridden using the `options` argument of Popper.js.<br />
2379
 * To override an option, simply pass an object with the same
2380
 * structure of the `options` object, as the 3rd argument. For example:
2381
 * ```
2382
 * new Popper(ref, pop, {
2383
 *   modifiers: {
2384
 *     preventOverflow: { enabled: false }
2385
 *   }
2386
 * })
2387
 * ```
2388
 * @type {Object}
2389
 * @static
2390
 * @memberof Popper
2391
 */
2392
var Defaults = {
2393
  /**
2394
   * Popper's placement.
2395
   * @prop {Popper.placements} placement='bottom'
2396
   */
2397
  placement: 'bottom',
2398
2399
  /**
2400
   * Set this to true if you want popper to position it self in 'fixed' mode
2401
   * @prop {Boolean} positionFixed=false
2402
   */
2403
  positionFixed: false,
2404
2405
  /**
2406
   * Whether events (resize, scroll) are initially enabled.
2407
   * @prop {Boolean} eventsEnabled=true
2408
   */
2409
  eventsEnabled: true,
2410
2411
  /**
2412
   * Set to true if you want to automatically remove the popper when
2413
   * you call the `destroy` method.
2414
   * @prop {Boolean} removeOnDestroy=false
2415
   */
2416
  removeOnDestroy: false,
2417
2418
  /**
2419
   * Callback called when the popper is created.<br />
2420
   * By default, it is set to no-op.<br />
2421
   * Access Popper.js instance with `data.instance`.
2422
   * @prop {onCreate}
2423
   */
2424
  onCreate: function onCreate() {},
2425
2426
  /**
2427
   * Callback called when the popper is updated. This callback is not called
2428
   * on the initialization/creation of the popper, but only on subsequent
2429
   * updates.<br />
2430
   * By default, it is set to no-op.<br />
2431
   * Access Popper.js instance with `data.instance`.
2432
   * @prop {onUpdate}
2433
   */
2434
  onUpdate: function onUpdate() {},
2435
2436
  /**
2437
   * List of modifiers used to modify the offsets before they are applied to the popper.
2438
   * They provide most of the functionalities of Popper.js.
2439
   * @prop {modifiers}
2440
   */
2441
  modifiers: modifiers
2442
};
2443
2444
/**
2445
 * @callback onCreate
2446
 * @param {dataObject} data
2447
 */
2448
2449
/**
2450
 * @callback onUpdate
2451
 * @param {dataObject} data
2452
 */
2453
2454
// Utils
2455
// Methods
2456
var Popper = function () {
2457
  /**
2458
   * Creates a new Popper.js instance.
2459
   * @class Popper
2460
   * @param {Element|referenceObject} reference - The reference element used to position the popper
2461
   * @param {Element} popper - The HTML / XML element used as the popper
2462
   * @param {Object} options - Your custom options to override the ones defined in [Defaults](#defaults)
2463
   * @return {Object} instance - The generated Popper.js instance
2464
   */
2465
  function Popper(reference, popper) {
2466
    var _this = this;
2467
2468
    var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
2469
    classCallCheck(this, Popper);
2470
2471
    this.scheduleUpdate = function () {
2472
      return requestAnimationFrame(_this.update);
2473
    };
2474
2475
    // make update() debounced, so that it only runs at most once-per-tick
2476
    this.update = debounce(this.update.bind(this));
2477
2478
    // with {} we create a new object with the options inside it
2479
    this.options = _extends({}, Popper.Defaults, options);
2480
2481
    // init state
2482
    this.state = {
2483
      isDestroyed: false,
2484
      isCreated: false,
2485
      scrollParents: []
2486
    };
2487
2488
    // get reference and popper elements (allow jQuery wrappers)
2489
    this.reference = reference && reference.jquery ? reference[0] : reference;
2490
    this.popper = popper && popper.jquery ? popper[0] : popper;
2491
2492
    // Deep merge modifiers options
2493
    this.options.modifiers = {};
2494
    Object.keys(_extends({}, Popper.Defaults.modifiers, options.modifiers)).forEach(function (name) {
2495
      _this.options.modifiers[name] = _extends({}, Popper.Defaults.modifiers[name] || {}, options.modifiers ? options.modifiers[name] : {});
2496
    });
2497
2498
    // Refactoring modifiers' list (Object => Array)
2499
    this.modifiers = Object.keys(this.options.modifiers).map(function (name) {
2500
      return _extends({
2501
        name: name
2502
      }, _this.options.modifiers[name]);
2503
    })
2504
    // sort the modifiers by order
2505
    .sort(function (a, b) {
2506
      return a.order - b.order;
2507
    });
2508
2509
    // modifiers have the ability to execute arbitrary code when Popper.js get inited
2510
    // such code is executed in the same order of its modifier
2511
    // they could add new properties to their options configuration
2512
    // BE AWARE: don't add options to `options.modifiers.name` but to `modifierOptions`!
2513
    this.modifiers.forEach(function (modifierOptions) {
2514
      if (modifierOptions.enabled && isFunction(modifierOptions.onLoad)) {
2515
        modifierOptions.onLoad(_this.reference, _this.popper, _this.options, modifierOptions, _this.state);
2516
      }
2517
    });
2518
2519
    // fire the first update to position the popper in the right place
2520
    this.update();
2521
2522
    var eventsEnabled = this.options.eventsEnabled;
2523
    if (eventsEnabled) {
2524
      // setup event listeners, they will take care of update the position in specific situations
2525
      this.enableEventListeners();
2526
    }
2527
2528
    this.state.eventsEnabled = eventsEnabled;
2529
  }
2530
2531
  // We can't use class properties because they don't get listed in the
2532
  // class prototype and break stuff like Sinon stubs
2533
2534
2535
  createClass(Popper, [{
2536
    key: 'update',
2537
    value: function update$$1() {
2538
      return update.call(this);
2539
    }
2540
  }, {
2541
    key: 'destroy',
2542
    value: function destroy$$1() {
2543
      return destroy.call(this);
2544
    }
2545
  }, {
2546
    key: 'enableEventListeners',
2547
    value: function enableEventListeners$$1() {
2548
      return enableEventListeners.call(this);
2549
    }
2550
  }, {
2551
    key: 'disableEventListeners',
2552
    value: function disableEventListeners$$1() {
2553
      return disableEventListeners.call(this);
2554
    }
2555
2556
    /**
2557
     * Schedules an update. It will run on the next UI update available.
2558
     * @method scheduleUpdate
2559
     * @memberof Popper
2560
     */
2561
2562
2563
    /**
2564
     * Collection of utilities useful when writing custom modifiers.
2565
     * Starting from version 1.7, this method is available only if you
2566
     * include `popper-utils.js` before `popper.js`.
2567
     *
2568
     * **DEPRECATION**: This way to access PopperUtils is deprecated
2569
     * and will be removed in v2! Use the PopperUtils module directly instead.
2570
     * Due to the high instability of the methods contained in Utils, we can't
2571
     * guarantee them to follow semver. Use them at your own risk!
2572
     * @static
2573
     * @private
2574
     * @type {Object}
2575
     * @deprecated since version 1.8
2576
     * @member Utils
2577
     * @memberof Popper
2578
     */
2579
2580
  }]);
2581
  return Popper;
2582
}();
2583
2584
/**
2585
 * The `referenceObject` is an object that provides an interface compatible with Popper.js
2586
 * and lets you use it as replacement of a real DOM node.<br />
2587
 * You can use this method to position a popper relatively to a set of coordinates
2588
 * in case you don't have a DOM node to use as reference.
2589
 *
2590
 * ```
2591
 * new Popper(referenceObject, popperNode);
2592
 * ```
2593
 *
2594
 * NB: This feature isn't supported in Internet Explorer 10.
2595
 * @name referenceObject
2596
 * @property {Function} data.getBoundingClientRect
2597
 * A function that returns a set of coordinates compatible with the native `getBoundingClientRect` method.
2598
 * @property {number} data.clientWidth
2599
 * An ES6 getter that will return the width of the virtual reference element.
2600
 * @property {number} data.clientHeight
2601
 * An ES6 getter that will return the height of the virtual reference element.
2602
 */
2603
2604
2605
Popper.Utils = (typeof window !== 'undefined' ? window : global).PopperUtils;
2606
Popper.placements = placements;
2607
Popper.Defaults = Defaults;
2608
2609
return Popper;
2610
2611
})));
2612
//# sourceMappingURL=popper.js.map
2613