GPQAPP

Форк
0
2462 строки · 82.9 Кб
1
/**!
2
 * @fileOverview Kickass library to create and place poppers near their reference elements.
3
 * @version 1.16.1
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
var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && typeof navigator !== 'undefined';
26

27
const timeoutDuration = function () {
28
  const longerTimeoutBrowsers = ['Edge', 'Trident', 'Firefox'];
29
  for (let i = 0; i < longerTimeoutBrowsers.length; i += 1) {
30
    if (isBrowser && navigator.userAgent.indexOf(longerTimeoutBrowsers[i]) >= 0) {
31
      return 1;
32
    }
33
  }
34
  return 0;
35
}();
36

37
function microtaskDebounce(fn) {
38
  let called = false;
39
  return () => {
40
    if (called) {
41
      return;
42
    }
43
    called = true;
44
    window.Promise.resolve().then(() => {
45
      called = false;
46
      fn();
47
    });
48
  };
49
}
50

51
function taskDebounce(fn) {
52
  let scheduled = false;
53
  return () => {
54
    if (!scheduled) {
55
      scheduled = true;
56
      setTimeout(() => {
57
        scheduled = false;
58
        fn();
59
      }, timeoutDuration);
60
    }
61
  };
62
}
63

64
const supportsMicroTasks = isBrowser && window.Promise;
65

66
/**
67
* Create a debounced version of a method, that's asynchronously deferred
68
* but called in the minimum time possible.
69
*
70
* @method
71
* @memberof Popper.Utils
72
* @argument {Function} fn
73
* @returns {Function}
74
*/
75
var debounce = supportsMicroTasks ? microtaskDebounce : taskDebounce;
76

77
/**
78
 * Check if the given variable is a function
79
 * @method
80
 * @memberof Popper.Utils
81
 * @argument {Any} functionToCheck - variable to check
82
 * @returns {Boolean} answer to: is a function?
83
 */
84
function isFunction(functionToCheck) {
85
  const getType = {};
86
  return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
87
}
88

89
/**
90
 * Get CSS computed property of the given element
91
 * @method
92
 * @memberof Popper.Utils
93
 * @argument {Eement} element
94
 * @argument {String} property
95
 */
96
function getStyleComputedProperty(element, property) {
97
  if (element.nodeType !== 1) {
98
    return [];
99
  }
100
  // NOTE: 1 DOM access here
101
  const window = element.ownerDocument.defaultView;
102
  const css = window.getComputedStyle(element, null);
103
  return property ? css[property] : css;
104
}
105

106
/**
107
 * Returns the parentNode or the host of the element
108
 * @method
109
 * @memberof Popper.Utils
110
 * @argument {Element} element
111
 * @returns {Element} parent
112
 */
113
function getParentNode(element) {
114
  if (element.nodeName === 'HTML') {
115
    return element;
116
  }
117
  return element.parentNode || element.host;
118
}
119

120
/**
121
 * Returns the scrolling parent of the given element
122
 * @method
123
 * @memberof Popper.Utils
124
 * @argument {Element} element
125
 * @returns {Element} scroll parent
126
 */
127
function getScrollParent(element) {
128
  // Return body, `getScroll` will take care to get the correct `scrollTop` from it
129
  if (!element) {
130
    return document.body;
131
  }
132

133
  switch (element.nodeName) {
134
    case 'HTML':
135
    case 'BODY':
136
      return element.ownerDocument.body;
137
    case '#document':
138
      return element.body;
139
  }
140

141
  // Firefox want us to check `-x` and `-y` variations as well
142
  const { overflow, overflowX, overflowY } = getStyleComputedProperty(element);
143
  if (/(auto|scroll|overlay)/.test(overflow + overflowY + overflowX)) {
144
    return element;
145
  }
146

147
  return getScrollParent(getParentNode(element));
148
}
149

150
/**
151
 * Returns the reference node of the reference object, or the reference object itself.
152
 * @method
153
 * @memberof Popper.Utils
154
 * @param {Element|Object} reference - the reference element (the popper will be relative to this)
155
 * @returns {Element} parent
156
 */
157
function getReferenceNode(reference) {
158
  return reference && reference.referenceNode ? reference.referenceNode : reference;
159
}
160

161
const isIE11 = isBrowser && !!(window.MSInputMethodContext && document.documentMode);
162
const isIE10 = isBrowser && /MSIE 10/.test(navigator.userAgent);
163

164
/**
165
 * Determines if the browser is Internet Explorer
166
 * @method
167
 * @memberof Popper.Utils
168
 * @param {Number} version to check
169
 * @returns {Boolean} isIE
170
 */
171
function isIE(version) {
172
  if (version === 11) {
173
    return isIE11;
174
  }
175
  if (version === 10) {
176
    return isIE10;
177
  }
178
  return isIE11 || isIE10;
179
}
180

181
/**
182
 * Returns the offset parent of the given element
183
 * @method
184
 * @memberof Popper.Utils
185
 * @argument {Element} element
186
 * @returns {Element} offset parent
187
 */
188
function getOffsetParent(element) {
189
  if (!element) {
190
    return document.documentElement;
191
  }
192

193
  const noOffsetParent = isIE(10) ? document.body : null;
194

195
  // NOTE: 1 DOM access here
196
  let offsetParent = element.offsetParent || null;
197
  // Skip hidden elements which don't have an offsetParent
198
  while (offsetParent === noOffsetParent && element.nextElementSibling) {
199
    offsetParent = (element = element.nextElementSibling).offsetParent;
200
  }
201

202
  const nodeName = offsetParent && offsetParent.nodeName;
203

204
  if (!nodeName || nodeName === 'BODY' || nodeName === 'HTML') {
205
    return element ? element.ownerDocument.documentElement : document.documentElement;
206
  }
207

208
  // .offsetParent will return the closest TH, TD or TABLE in case
209
  // no offsetParent is present, I hate this job...
210
  if (['TH', 'TD', 'TABLE'].indexOf(offsetParent.nodeName) !== -1 && getStyleComputedProperty(offsetParent, 'position') === 'static') {
211
    return getOffsetParent(offsetParent);
212
  }
213

214
  return offsetParent;
215
}
216

217
function isOffsetContainer(element) {
218
  const { nodeName } = element;
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
  const order = element1.compareDocumentPosition(element2) & Node.DOCUMENT_POSITION_FOLLOWING;
256
  const start = order ? element1 : element2;
257
  const end = order ? element2 : element1;
258

259
  // Get common ancestor container
260
  const range = document.createRange();
261
  range.setStart(start, 0);
262
  range.setEnd(end, 0);
263
  const { commonAncestorContainer } = range;
264

265
  // Both nodes are inside #document
266
  if (element1 !== commonAncestorContainer && element2 !== commonAncestorContainer || start.contains(end)) {
267
    if (isOffsetContainer(commonAncestorContainer)) {
268
      return commonAncestorContainer;
269
    }
270

271
    return getOffsetParent(commonAncestorContainer);
272
  }
273

274
  // one of the nodes is inside shadowDOM, find which one
275
  const element1root = getRoot(element1);
276
  if (element1root.host) {
277
    return findCommonOffsetParent(element1root.host, element2);
278
  } else {
279
    return findCommonOffsetParent(element1, getRoot(element2).host);
280
  }
281
}
282

283
/**
284
 * Gets the scroll value of the given element in the given side (top and left)
285
 * @method
286
 * @memberof Popper.Utils
287
 * @argument {Element} element
288
 * @argument {String} side `top` or `left`
289
 * @returns {number} amount of scrolled pixels
290
 */
291
function getScroll(element, side = 'top') {
292
  const upperSide = side === 'top' ? 'scrollTop' : 'scrollLeft';
293
  const nodeName = element.nodeName;
294

295
  if (nodeName === 'BODY' || nodeName === 'HTML') {
296
    const html = element.ownerDocument.documentElement;
297
    const scrollingElement = element.ownerDocument.scrollingElement || html;
298
    return scrollingElement[upperSide];
299
  }
300

301
  return element[upperSide];
302
}
303

304
/*
305
 * Sum or subtract the element scroll values (left and top) from a given rect object
306
 * @method
307
 * @memberof Popper.Utils
308
 * @param {Object} rect - Rect object you want to change
309
 * @param {HTMLElement} element - The element from the function reads the scroll values
310
 * @param {Boolean} subtract - set to true if you want to subtract the scroll values
311
 * @return {Object} rect - The modifier rect object
312
 */
313
function includeScroll(rect, element, subtract = false) {
314
  const scrollTop = getScroll(element, 'top');
315
  const scrollLeft = getScroll(element, 'left');
316
  const modifier = subtract ? -1 : 1;
317
  rect.top += scrollTop * modifier;
318
  rect.bottom += scrollTop * modifier;
319
  rect.left += scrollLeft * modifier;
320
  rect.right += scrollLeft * modifier;
321
  return rect;
322
}
323

324
/*
325
 * Helper to detect borders of a given element
326
 * @method
327
 * @memberof Popper.Utils
328
 * @param {CSSStyleDeclaration} styles
329
 * Result of `getStyleComputedProperty` on the given element
330
 * @param {String} axis - `x` or `y`
331
 * @return {number} borders - The borders size of the given axis
332
 */
333

334
function getBordersSize(styles, axis) {
335
  const sideA = axis === 'x' ? 'Left' : 'Top';
336
  const sideB = sideA === 'Left' ? 'Right' : 'Bottom';
337

338
  return parseFloat(styles[`border${sideA}Width`]) + parseFloat(styles[`border${sideB}Width`]);
339
}
340

341
function getSize(axis, body, html, computedStyle) {
342
  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);
343
}
344

345
function getWindowSizes(document) {
346
  const body = document.body;
347
  const html = document.documentElement;
348
  const computedStyle = isIE(10) && getComputedStyle(html);
349

350
  return {
351
    height: getSize('Height', body, html, computedStyle),
352
    width: getSize('Width', body, html, computedStyle)
353
  };
354
}
355

356
var _extends = Object.assign || function (target) {
357
  for (var i = 1; i < arguments.length; i++) {
358
    var source = arguments[i];
359

360
    for (var key in source) {
361
      if (Object.prototype.hasOwnProperty.call(source, key)) {
362
        target[key] = source[key];
363
      }
364
    }
365
  }
366

367
  return target;
368
};
369

370
/**
371
 * Given element offsets, generate an output similar to getBoundingClientRect
372
 * @method
373
 * @memberof Popper.Utils
374
 * @argument {Object} offsets
375
 * @returns {Object} ClientRect like output
376
 */
377
function getClientRect(offsets) {
378
  return _extends({}, offsets, {
379
    right: offsets.left + offsets.width,
380
    bottom: offsets.top + offsets.height
381
  });
382
}
383

384
/**
385
 * Get bounding client rect of given element
386
 * @method
387
 * @memberof Popper.Utils
388
 * @param {HTMLElement} element
389
 * @return {Object} client rect
390
 */
391
function getBoundingClientRect(element) {
392
  let rect = {};
393

394
  // IE10 10 FIX: Please, don't ask, the element isn't
395
  // considered in DOM in some circumstances...
396
  // This isn't reproducible in IE10 compatibility mode of IE11
397
  try {
398
    if (isIE(10)) {
399
      rect = element.getBoundingClientRect();
400
      const scrollTop = getScroll(element, 'top');
401
      const scrollLeft = getScroll(element, 'left');
402
      rect.top += scrollTop;
403
      rect.left += scrollLeft;
404
      rect.bottom += scrollTop;
405
      rect.right += scrollLeft;
406
    } else {
407
      rect = element.getBoundingClientRect();
408
    }
409
  } catch (e) {}
410

411
  const result = {
412
    left: rect.left,
413
    top: rect.top,
414
    width: rect.right - rect.left,
415
    height: rect.bottom - rect.top
416
  };
417

418
  // subtract scrollbar size from sizes
419
  const sizes = element.nodeName === 'HTML' ? getWindowSizes(element.ownerDocument) : {};
420
  const width = sizes.width || element.clientWidth || result.width;
421
  const height = sizes.height || element.clientHeight || result.height;
422

423
  let horizScrollbar = element.offsetWidth - width;
424
  let vertScrollbar = element.offsetHeight - height;
425

426
  // if an hypothetical scrollbar is detected, we must be sure it's not a `border`
427
  // we make this check conditional for performance reasons
428
  if (horizScrollbar || vertScrollbar) {
429
    const styles = getStyleComputedProperty(element);
430
    horizScrollbar -= getBordersSize(styles, 'x');
431
    vertScrollbar -= getBordersSize(styles, 'y');
432

433
    result.width -= horizScrollbar;
434
    result.height -= vertScrollbar;
435
  }
436

437
  return getClientRect(result);
438
}
439

440
function getOffsetRectRelativeToArbitraryNode(children, parent, fixedPosition = false) {
441
  const isIE10 = isIE(10);
442
  const isHTML = parent.nodeName === 'HTML';
443
  const childrenRect = getBoundingClientRect(children);
444
  const parentRect = getBoundingClientRect(parent);
445
  const scrollParent = getScrollParent(children);
446

447
  const styles = getStyleComputedProperty(parent);
448
  const borderTopWidth = parseFloat(styles.borderTopWidth);
449
  const borderLeftWidth = parseFloat(styles.borderLeftWidth);
450

451
  // In cases where the parent is fixed, we must ignore negative scroll in offset calc
452
  if (fixedPosition && isHTML) {
453
    parentRect.top = Math.max(parentRect.top, 0);
454
    parentRect.left = Math.max(parentRect.left, 0);
455
  }
456
  let offsets = getClientRect({
457
    top: childrenRect.top - parentRect.top - borderTopWidth,
458
    left: childrenRect.left - parentRect.left - borderLeftWidth,
459
    width: childrenRect.width,
460
    height: childrenRect.height
461
  });
462
  offsets.marginTop = 0;
463
  offsets.marginLeft = 0;
464

465
  // Subtract margins of documentElement in case it's being used as parent
466
  // we do this only on HTML because it's the only element that behaves
467
  // differently when margins are applied to it. The margins are included in
468
  // the box of the documentElement, in the other cases not.
469
  if (!isIE10 && isHTML) {
470
    const marginTop = parseFloat(styles.marginTop);
471
    const marginLeft = parseFloat(styles.marginLeft);
472

473
    offsets.top -= borderTopWidth - marginTop;
474
    offsets.bottom -= borderTopWidth - marginTop;
475
    offsets.left -= borderLeftWidth - marginLeft;
476
    offsets.right -= borderLeftWidth - marginLeft;
477

478
    // Attach marginTop and marginLeft because in some circumstances we may need them
479
    offsets.marginTop = marginTop;
480
    offsets.marginLeft = marginLeft;
481
  }
482

483
  if (isIE10 && !fixedPosition ? parent.contains(scrollParent) : parent === scrollParent && scrollParent.nodeName !== 'BODY') {
484
    offsets = includeScroll(offsets, parent);
485
  }
486

487
  return offsets;
488
}
489

490
function getViewportOffsetRectRelativeToArtbitraryNode(element, excludeScroll = false) {
491
  const html = element.ownerDocument.documentElement;
492
  const relativeOffset = getOffsetRectRelativeToArbitraryNode(element, html);
493
  const width = Math.max(html.clientWidth, window.innerWidth || 0);
494
  const height = Math.max(html.clientHeight, window.innerHeight || 0);
495

496
  const scrollTop = !excludeScroll ? getScroll(html) : 0;
497
  const scrollLeft = !excludeScroll ? getScroll(html, 'left') : 0;
498

499
  const offset = {
500
    top: scrollTop - relativeOffset.top + relativeOffset.marginTop,
501
    left: scrollLeft - relativeOffset.left + relativeOffset.marginLeft,
502
    width,
503
    height
504
  };
505

506
  return getClientRect(offset);
507
}
508

509
/**
510
 * Check if the given element is fixed or is inside a fixed parent
511
 * @method
512
 * @memberof Popper.Utils
513
 * @argument {Element} element
514
 * @argument {Element} customContainer
515
 * @returns {Boolean} answer to "isFixed?"
516
 */
517
function isFixed(element) {
518
  const nodeName = element.nodeName;
519
  if (nodeName === 'BODY' || nodeName === 'HTML') {
520
    return false;
521
  }
522
  if (getStyleComputedProperty(element, 'position') === 'fixed') {
523
    return true;
524
  }
525
  const parentNode = getParentNode(element);
526
  if (!parentNode) {
527
    return false;
528
  }
529
  return isFixed(parentNode);
530
}
531

532
/**
533
 * Finds the first parent of an element that has a transformed property defined
534
 * @method
535
 * @memberof Popper.Utils
536
 * @argument {Element} element
537
 * @returns {Element} first transformed parent or documentElement
538
 */
539

540
function getFixedPositionOffsetParent(element) {
541
  // This check is needed to avoid errors in case one of the elements isn't defined for any reason
542
  if (!element || !element.parentElement || isIE()) {
543
    return document.documentElement;
544
  }
545
  let el = element.parentElement;
546
  while (el && getStyleComputedProperty(el, 'transform') === 'none') {
547
    el = el.parentElement;
548
  }
549
  return el || document.documentElement;
550
}
551

552
/**
553
 * Computed the boundaries limits and return them
554
 * @method
555
 * @memberof Popper.Utils
556
 * @param {HTMLElement} popper
557
 * @param {HTMLElement} reference
558
 * @param {number} padding
559
 * @param {HTMLElement} boundariesElement - Element used to define the boundaries
560
 * @param {Boolean} fixedPosition - Is in fixed position mode
561
 * @returns {Object} Coordinates of the boundaries
562
 */
563
function getBoundaries(popper, reference, padding, boundariesElement, fixedPosition = false) {
564
  // NOTE: 1 DOM access here
565

566
  let boundaries = { top: 0, left: 0 };
567
  const offsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, getReferenceNode(reference));
568

569
  // Handle viewport case
570
  if (boundariesElement === 'viewport') {
571
    boundaries = getViewportOffsetRectRelativeToArtbitraryNode(offsetParent, fixedPosition);
572
  } else {
573
    // Handle other cases based on DOM element used as boundaries
574
    let boundariesNode;
575
    if (boundariesElement === 'scrollParent') {
576
      boundariesNode = getScrollParent(getParentNode(reference));
577
      if (boundariesNode.nodeName === 'BODY') {
578
        boundariesNode = popper.ownerDocument.documentElement;
579
      }
580
    } else if (boundariesElement === 'window') {
581
      boundariesNode = popper.ownerDocument.documentElement;
582
    } else {
583
      boundariesNode = boundariesElement;
584
    }
585

586
    const offsets = getOffsetRectRelativeToArbitraryNode(boundariesNode, offsetParent, fixedPosition);
587

588
    // In case of HTML, we need a different computation
589
    if (boundariesNode.nodeName === 'HTML' && !isFixed(offsetParent)) {
590
      const { height, width } = getWindowSizes(popper.ownerDocument);
591
      boundaries.top += offsets.top - offsets.marginTop;
592
      boundaries.bottom = height + offsets.top;
593
      boundaries.left += offsets.left - offsets.marginLeft;
594
      boundaries.right = width + offsets.left;
595
    } else {
596
      // for all the other DOM elements, this one is good
597
      boundaries = offsets;
598
    }
599
  }
600

601
  // Add paddings
602
  padding = padding || 0;
603
  const isPaddingNumber = typeof padding === 'number';
604
  boundaries.left += isPaddingNumber ? padding : padding.left || 0;
605
  boundaries.top += isPaddingNumber ? padding : padding.top || 0;
606
  boundaries.right -= isPaddingNumber ? padding : padding.right || 0;
607
  boundaries.bottom -= isPaddingNumber ? padding : padding.bottom || 0;
608

609
  return boundaries;
610
}
611

612
function getArea({ width, height }) {
613
  return width * height;
614
}
615

616
/**
617
 * Utility used to transform the `auto` placement to the placement with more
618
 * available space.
619
 * @method
620
 * @memberof Popper.Utils
621
 * @argument {Object} data - The data object generated by update method
622
 * @argument {Object} options - Modifiers configuration and options
623
 * @returns {Object} The data object, properly modified
624
 */
625
function computeAutoPlacement(placement, refRect, popper, reference, boundariesElement, padding = 0) {
626
  if (placement.indexOf('auto') === -1) {
627
    return placement;
628
  }
629

630
  const boundaries = getBoundaries(popper, reference, padding, boundariesElement);
631

632
  const rects = {
633
    top: {
634
      width: boundaries.width,
635
      height: refRect.top - boundaries.top
636
    },
637
    right: {
638
      width: boundaries.right - refRect.right,
639
      height: boundaries.height
640
    },
641
    bottom: {
642
      width: boundaries.width,
643
      height: boundaries.bottom - refRect.bottom
644
    },
645
    left: {
646
      width: refRect.left - boundaries.left,
647
      height: boundaries.height
648
    }
649
  };
650

651
  const sortedAreas = Object.keys(rects).map(key => _extends({
652
    key
653
  }, rects[key], {
654
    area: getArea(rects[key])
655
  })).sort((a, b) => b.area - a.area);
656

657
  const filteredAreas = sortedAreas.filter(({ width, height }) => width >= popper.clientWidth && height >= popper.clientHeight);
658

659
  const computedPlacement = filteredAreas.length > 0 ? filteredAreas[0].key : sortedAreas[0].key;
660

661
  const variation = placement.split('-')[1];
662

663
  return computedPlacement + (variation ? `-${variation}` : '');
664
}
665

666
/**
667
 * Get offsets to the reference element
668
 * @method
669
 * @memberof Popper.Utils
670
 * @param {Object} state
671
 * @param {Element} popper - the popper element
672
 * @param {Element} reference - the reference element (the popper will be relative to this)
673
 * @param {Element} fixedPosition - is in fixed position mode
674
 * @returns {Object} An object containing the offsets which will be applied to the popper
675
 */
676
function getReferenceOffsets(state, popper, reference, fixedPosition = null) {
677
  const commonOffsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, getReferenceNode(reference));
678
  return getOffsetRectRelativeToArbitraryNode(reference, commonOffsetParent, fixedPosition);
679
}
680

681
/**
682
 * Get the outer sizes of the given element (offset size + margins)
683
 * @method
684
 * @memberof Popper.Utils
685
 * @argument {Element} element
686
 * @returns {Object} object containing width and height properties
687
 */
688
function getOuterSizes(element) {
689
  const window = element.ownerDocument.defaultView;
690
  const styles = window.getComputedStyle(element);
691
  const x = parseFloat(styles.marginTop || 0) + parseFloat(styles.marginBottom || 0);
692
  const y = parseFloat(styles.marginLeft || 0) + parseFloat(styles.marginRight || 0);
693
  const result = {
694
    width: element.offsetWidth + y,
695
    height: element.offsetHeight + x
696
  };
697
  return result;
698
}
699

700
/**
701
 * Get the opposite placement of the given one
702
 * @method
703
 * @memberof Popper.Utils
704
 * @argument {String} placement
705
 * @returns {String} flipped placement
706
 */
707
function getOppositePlacement(placement) {
708
  const hash = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' };
709
  return placement.replace(/left|right|bottom|top/g, matched => hash[matched]);
710
}
711

712
/**
713
 * Get offsets to the popper
714
 * @method
715
 * @memberof Popper.Utils
716
 * @param {Object} position - CSS position the Popper will get applied
717
 * @param {HTMLElement} popper - the popper element
718
 * @param {Object} referenceOffsets - the reference offsets (the popper will be relative to this)
719
 * @param {String} placement - one of the valid placement options
720
 * @returns {Object} popperOffsets - An object containing the offsets which will be applied to the popper
721
 */
722
function getPopperOffsets(popper, referenceOffsets, placement) {
723
  placement = placement.split('-')[0];
724

725
  // Get popper node sizes
726
  const popperRect = getOuterSizes(popper);
727

728
  // Add position, width and height to our offsets object
729
  const popperOffsets = {
730
    width: popperRect.width,
731
    height: popperRect.height
732
  };
733

734
  // depending by the popper placement we have to compute its offsets slightly differently
735
  const isHoriz = ['right', 'left'].indexOf(placement) !== -1;
736
  const mainSide = isHoriz ? 'top' : 'left';
737
  const secondarySide = isHoriz ? 'left' : 'top';
738
  const measurement = isHoriz ? 'height' : 'width';
739
  const secondaryMeasurement = !isHoriz ? 'height' : 'width';
740

741
  popperOffsets[mainSide] = referenceOffsets[mainSide] + referenceOffsets[measurement] / 2 - popperRect[measurement] / 2;
742
  if (placement === secondarySide) {
743
    popperOffsets[secondarySide] = referenceOffsets[secondarySide] - popperRect[secondaryMeasurement];
744
  } else {
745
    popperOffsets[secondarySide] = referenceOffsets[getOppositePlacement(secondarySide)];
746
  }
747

748
  return popperOffsets;
749
}
750

751
/**
752
 * Mimics the `find` method of Array
753
 * @method
754
 * @memberof Popper.Utils
755
 * @argument {Array} arr
756
 * @argument prop
757
 * @argument value
758
 * @returns index or -1
759
 */
760
function find(arr, check) {
761
  // use native find if supported
762
  if (Array.prototype.find) {
763
    return arr.find(check);
764
  }
765

766
  // use `filter` to obtain the same behavior of `find`
767
  return arr.filter(check)[0];
768
}
769

770
/**
771
 * Return the index of the matching object
772
 * @method
773
 * @memberof Popper.Utils
774
 * @argument {Array} arr
775
 * @argument prop
776
 * @argument value
777
 * @returns index or -1
778
 */
779
function findIndex(arr, prop, value) {
780
  // use native findIndex if supported
781
  if (Array.prototype.findIndex) {
782
    return arr.findIndex(cur => cur[prop] === value);
783
  }
784

785
  // use `find` + `indexOf` if `findIndex` isn't supported
786
  const match = find(arr, obj => obj[prop] === value);
787
  return arr.indexOf(match);
788
}
789

790
/**
791
 * Loop trough the list of modifiers and run them in order,
792
 * each of them will then edit the data object.
793
 * @method
794
 * @memberof Popper.Utils
795
 * @param {dataObject} data
796
 * @param {Array} modifiers
797
 * @param {String} ends - Optional modifier name used as stopper
798
 * @returns {dataObject}
799
 */
800
function runModifiers(modifiers, data, ends) {
801
  const modifiersToRun = ends === undefined ? modifiers : modifiers.slice(0, findIndex(modifiers, 'name', ends));
802

803
  modifiersToRun.forEach(modifier => {
804
    if (modifier['function']) {
805
      // eslint-disable-line dot-notation
806
      console.warn('`modifier.function` is deprecated, use `modifier.fn`!');
807
    }
808
    const fn = modifier['function'] || modifier.fn; // eslint-disable-line dot-notation
809
    if (modifier.enabled && isFunction(fn)) {
810
      // Add properties to offsets to make them a complete clientRect object
811
      // we do this before each modifier to make sure the previous one doesn't
812
      // mess with these values
813
      data.offsets.popper = getClientRect(data.offsets.popper);
814
      data.offsets.reference = getClientRect(data.offsets.reference);
815

816
      data = fn(data, modifier);
817
    }
818
  });
819

820
  return data;
821
}
822

823
/**
824
 * Updates the position of the popper, computing the new offsets and applying
825
 * the new style.<br />
826
 * Prefer `scheduleUpdate` over `update` because of performance reasons.
827
 * @method
828
 * @memberof Popper
829
 */
830
function update() {
831
  // if popper is destroyed, don't perform any further update
832
  if (this.state.isDestroyed) {
833
    return;
834
  }
835

836
  let data = {
837
    instance: this,
838
    styles: {},
839
    arrowStyles: {},
840
    attributes: {},
841
    flipped: false,
842
    offsets: {}
843
  };
844

845
  // compute reference element offsets
846
  data.offsets.reference = getReferenceOffsets(this.state, this.popper, this.reference, this.options.positionFixed);
847

848
  // compute auto placement, store placement inside the data object,
849
  // modifiers will be able to edit `placement` if needed
850
  // and refer to originalPlacement to know the original value
851
  data.placement = computeAutoPlacement(this.options.placement, data.offsets.reference, this.popper, this.reference, this.options.modifiers.flip.boundariesElement, this.options.modifiers.flip.padding);
852

853
  // store the computed placement inside `originalPlacement`
854
  data.originalPlacement = data.placement;
855

856
  data.positionFixed = this.options.positionFixed;
857

858
  // compute the popper offsets
859
  data.offsets.popper = getPopperOffsets(this.popper, data.offsets.reference, data.placement);
860

861
  data.offsets.popper.position = this.options.positionFixed ? 'fixed' : 'absolute';
862

863
  // run the modifiers
864
  data = runModifiers(this.modifiers, data);
865

866
  // the first `update` will call `onCreate` callback
867
  // the other ones will call `onUpdate` callback
868
  if (!this.state.isCreated) {
869
    this.state.isCreated = true;
870
    this.options.onCreate(data);
871
  } else {
872
    this.options.onUpdate(data);
873
  }
874
}
875

876
/**
877
 * Helper used to know if the given modifier is enabled.
878
 * @method
879
 * @memberof Popper.Utils
880
 * @returns {Boolean}
881
 */
882
function isModifierEnabled(modifiers, modifierName) {
883
  return modifiers.some(({ name, enabled }) => enabled && name === modifierName);
884
}
885

886
/**
887
 * Get the prefixed supported property name
888
 * @method
889
 * @memberof Popper.Utils
890
 * @argument {String} property (camelCase)
891
 * @returns {String} prefixed property (camelCase or PascalCase, depending on the vendor prefix)
892
 */
893
function getSupportedPropertyName(property) {
894
  const prefixes = [false, 'ms', 'Webkit', 'Moz', 'O'];
895
  const upperProp = property.charAt(0).toUpperCase() + property.slice(1);
896

897
  for (let i = 0; i < prefixes.length; i++) {
898
    const prefix = prefixes[i];
899
    const toCheck = prefix ? `${prefix}${upperProp}` : property;
900
    if (typeof document.body.style[toCheck] !== 'undefined') {
901
      return toCheck;
902
    }
903
  }
904
  return null;
905
}
906

907
/**
908
 * Destroys the popper.
909
 * @method
910
 * @memberof Popper
911
 */
912
function destroy() {
913
  this.state.isDestroyed = true;
914

915
  // touch DOM only if `applyStyle` modifier is enabled
916
  if (isModifierEnabled(this.modifiers, 'applyStyle')) {
917
    this.popper.removeAttribute('x-placement');
918
    this.popper.style.position = '';
919
    this.popper.style.top = '';
920
    this.popper.style.left = '';
921
    this.popper.style.right = '';
922
    this.popper.style.bottom = '';
923
    this.popper.style.willChange = '';
924
    this.popper.style[getSupportedPropertyName('transform')] = '';
925
  }
926

927
  this.disableEventListeners();
928

929
  // remove the popper if user explicitly asked for the deletion on destroy
930
  // do not use `remove` because IE11 doesn't support it
931
  if (this.options.removeOnDestroy) {
932
    this.popper.parentNode.removeChild(this.popper);
933
  }
934
  return this;
935
}
936

937
/**
938
 * Get the window associated with the element
939
 * @argument {Element} element
940
 * @returns {Window}
941
 */
942
function getWindow(element) {
943
  const ownerDocument = element.ownerDocument;
944
  return ownerDocument ? ownerDocument.defaultView : window;
945
}
946

947
function attachToScrollParents(scrollParent, event, callback, scrollParents) {
948
  const isBody = scrollParent.nodeName === 'BODY';
949
  const target = isBody ? scrollParent.ownerDocument.defaultView : scrollParent;
950
  target.addEventListener(event, callback, { passive: true });
951

952
  if (!isBody) {
953
    attachToScrollParents(getScrollParent(target.parentNode), event, callback, scrollParents);
954
  }
955
  scrollParents.push(target);
956
}
957

958
/**
959
 * Setup needed event listeners used to update the popper position
960
 * @method
961
 * @memberof Popper.Utils
962
 * @private
963
 */
964
function setupEventListeners(reference, options, state, updateBound) {
965
  // Resize event listener on window
966
  state.updateBound = updateBound;
967
  getWindow(reference).addEventListener('resize', state.updateBound, { passive: true });
968

969
  // Scroll event listener on scroll parents
970
  const scrollElement = getScrollParent(reference);
971
  attachToScrollParents(scrollElement, 'scroll', state.updateBound, state.scrollParents);
972
  state.scrollElement = scrollElement;
973
  state.eventsEnabled = true;
974

975
  return state;
976
}
977

978
/**
979
 * It will add resize/scroll events and start recalculating
980
 * position of the popper element when they are triggered.
981
 * @method
982
 * @memberof Popper
983
 */
984
function enableEventListeners() {
985
  if (!this.state.eventsEnabled) {
986
    this.state = setupEventListeners(this.reference, this.options, this.state, this.scheduleUpdate);
987
  }
988
}
989

990
/**
991
 * Remove event listeners used to update the popper position
992
 * @method
993
 * @memberof Popper.Utils
994
 * @private
995
 */
996
function removeEventListeners(reference, state) {
997
  // Remove resize event listener on window
998
  getWindow(reference).removeEventListener('resize', state.updateBound);
999

1000
  // Remove scroll event listener on scroll parents
1001
  state.scrollParents.forEach(target => {
1002
    target.removeEventListener('scroll', state.updateBound);
1003
  });
1004

1005
  // Reset state
1006
  state.updateBound = null;
1007
  state.scrollParents = [];
1008
  state.scrollElement = null;
1009
  state.eventsEnabled = false;
1010
  return state;
1011
}
1012

1013
/**
1014
 * It will remove resize/scroll events and won't recalculate popper position
1015
 * when they are triggered. It also won't trigger `onUpdate` callback anymore,
1016
 * unless you call `update` method manually.
1017
 * @method
1018
 * @memberof Popper
1019
 */
1020
function disableEventListeners() {
1021
  if (this.state.eventsEnabled) {
1022
    cancelAnimationFrame(this.scheduleUpdate);
1023
    this.state = removeEventListeners(this.reference, this.state);
1024
  }
1025
}
1026

1027
/**
1028
 * Tells if a given input is a number
1029
 * @method
1030
 * @memberof Popper.Utils
1031
 * @param {*} input to check
1032
 * @return {Boolean}
1033
 */
1034
function isNumeric(n) {
1035
  return n !== '' && !isNaN(parseFloat(n)) && isFinite(n);
1036
}
1037

1038
/**
1039
 * Set the style to the given popper
1040
 * @method
1041
 * @memberof Popper.Utils
1042
 * @argument {Element} element - Element to apply the style to
1043
 * @argument {Object} styles
1044
 * Object with a list of properties and values which will be applied to the element
1045
 */
1046
function setStyles(element, styles) {
1047
  Object.keys(styles).forEach(prop => {
1048
    let unit = '';
1049
    // add unit if the value is numeric and is one of the following
1050
    if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && isNumeric(styles[prop])) {
1051
      unit = 'px';
1052
    }
1053
    element.style[prop] = styles[prop] + unit;
1054
  });
1055
}
1056

1057
/**
1058
 * Set the attributes to the given popper
1059
 * @method
1060
 * @memberof Popper.Utils
1061
 * @argument {Element} element - Element to apply the attributes to
1062
 * @argument {Object} styles
1063
 * Object with a list of properties and values which will be applied to the element
1064
 */
1065
function setAttributes(element, attributes) {
1066
  Object.keys(attributes).forEach(function (prop) {
1067
    const value = attributes[prop];
1068
    if (value !== false) {
1069
      element.setAttribute(prop, attributes[prop]);
1070
    } else {
1071
      element.removeAttribute(prop);
1072
    }
1073
  });
1074
}
1075

1076
/**
1077
 * @function
1078
 * @memberof Modifiers
1079
 * @argument {Object} data - The data object generated by `update` method
1080
 * @argument {Object} data.styles - List of style properties - values to apply to popper element
1081
 * @argument {Object} data.attributes - List of attribute properties - values to apply to popper element
1082
 * @argument {Object} options - Modifiers configuration and options
1083
 * @returns {Object} The same data object
1084
 */
1085
function applyStyle(data) {
1086
  // any property present in `data.styles` will be applied to the popper,
1087
  // in this way we can make the 3rd party modifiers add custom styles to it
1088
  // Be aware, modifiers could override the properties defined in the previous
1089
  // lines of this modifier!
1090
  setStyles(data.instance.popper, data.styles);
1091

1092
  // any property present in `data.attributes` will be applied to the popper,
1093
  // they will be set as HTML attributes of the element
1094
  setAttributes(data.instance.popper, data.attributes);
1095

1096
  // if arrowElement is defined and arrowStyles has some properties
1097
  if (data.arrowElement && Object.keys(data.arrowStyles).length) {
1098
    setStyles(data.arrowElement, data.arrowStyles);
1099
  }
1100

1101
  return data;
1102
}
1103

1104
/**
1105
 * Set the x-placement attribute before everything else because it could be used
1106
 * to add margins to the popper margins needs to be calculated to get the
1107
 * correct popper offsets.
1108
 * @method
1109
 * @memberof Popper.modifiers
1110
 * @param {HTMLElement} reference - The reference element used to position the popper
1111
 * @param {HTMLElement} popper - The HTML element used as popper
1112
 * @param {Object} options - Popper.js options
1113
 */
1114
function applyStyleOnLoad(reference, popper, options, modifierOptions, state) {
1115
  // compute reference element offsets
1116
  const referenceOffsets = getReferenceOffsets(state, popper, reference, options.positionFixed);
1117

1118
  // compute auto placement, store placement inside the data object,
1119
  // modifiers will be able to edit `placement` if needed
1120
  // and refer to originalPlacement to know the original value
1121
  const placement = computeAutoPlacement(options.placement, referenceOffsets, popper, reference, options.modifiers.flip.boundariesElement, options.modifiers.flip.padding);
1122

1123
  popper.setAttribute('x-placement', placement);
1124

1125
  // Apply `position` to popper before anything else because
1126
  // without the position applied we can't guarantee correct computations
1127
  setStyles(popper, { position: options.positionFixed ? 'fixed' : 'absolute' });
1128

1129
  return options;
1130
}
1131

1132
/**
1133
 * @function
1134
 * @memberof Popper.Utils
1135
 * @argument {Object} data - The data object generated by `update` method
1136
 * @argument {Boolean} shouldRound - If the offsets should be rounded at all
1137
 * @returns {Object} The popper's position offsets rounded
1138
 *
1139
 * The tale of pixel-perfect positioning. It's still not 100% perfect, but as
1140
 * good as it can be within reason.
1141
 * Discussion here: https://github.com/FezVrasta/popper.js/pull/715
1142
 *
1143
 * Low DPI screens cause a popper to be blurry if not using full pixels (Safari
1144
 * as well on High DPI screens).
1145
 *
1146
 * Firefox prefers no rounding for positioning and does not have blurriness on
1147
 * high DPI screens.
1148
 *
1149
 * Only horizontal placement and left/right values need to be considered.
1150
 */
1151
function getRoundedOffsets(data, shouldRound) {
1152
  const { popper, reference } = data.offsets;
1153
  const { round, floor } = Math;
1154
  const noRound = v => v;
1155

1156
  const referenceWidth = round(reference.width);
1157
  const popperWidth = round(popper.width);
1158

1159
  const isVertical = ['left', 'right'].indexOf(data.placement) !== -1;
1160
  const isVariation = data.placement.indexOf('-') !== -1;
1161
  const sameWidthParity = referenceWidth % 2 === popperWidth % 2;
1162
  const bothOddWidth = referenceWidth % 2 === 1 && popperWidth % 2 === 1;
1163

1164
  const horizontalToInteger = !shouldRound ? noRound : isVertical || isVariation || sameWidthParity ? round : floor;
1165
  const verticalToInteger = !shouldRound ? noRound : round;
1166

1167
  return {
1168
    left: horizontalToInteger(bothOddWidth && !isVariation && shouldRound ? popper.left - 1 : popper.left),
1169
    top: verticalToInteger(popper.top),
1170
    bottom: verticalToInteger(popper.bottom),
1171
    right: horizontalToInteger(popper.right)
1172
  };
1173
}
1174

1175
const isFirefox = isBrowser && /Firefox/i.test(navigator.userAgent);
1176

1177
/**
1178
 * @function
1179
 * @memberof Modifiers
1180
 * @argument {Object} data - The data object generated by `update` method
1181
 * @argument {Object} options - Modifiers configuration and options
1182
 * @returns {Object} The data object, properly modified
1183
 */
1184
function computeStyle(data, options) {
1185
  const { x, y } = options;
1186
  const { popper } = data.offsets;
1187

1188
  // Remove this legacy support in Popper.js v2
1189
  const legacyGpuAccelerationOption = find(data.instance.modifiers, modifier => modifier.name === 'applyStyle').gpuAcceleration;
1190
  if (legacyGpuAccelerationOption !== undefined) {
1191
    console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!');
1192
  }
1193
  const gpuAcceleration = legacyGpuAccelerationOption !== undefined ? legacyGpuAccelerationOption : options.gpuAcceleration;
1194

1195
  const offsetParent = getOffsetParent(data.instance.popper);
1196
  const offsetParentRect = getBoundingClientRect(offsetParent);
1197

1198
  // Styles
1199
  const styles = {
1200
    position: popper.position
1201
  };
1202

1203
  const offsets = getRoundedOffsets(data, window.devicePixelRatio < 2 || !isFirefox);
1204

1205
  const sideA = x === 'bottom' ? 'top' : 'bottom';
1206
  const sideB = y === 'right' ? 'left' : 'right';
1207

1208
  // if gpuAcceleration is set to `true` and transform is supported,
1209
  //  we use `translate3d` to apply the position to the popper we
1210
  // automatically use the supported prefixed version if needed
1211
  const prefixedProperty = getSupportedPropertyName('transform');
1212

1213
  // now, let's make a step back and look at this code closely (wtf?)
1214
  // If the content of the popper grows once it's been positioned, it
1215
  // may happen that the popper gets misplaced because of the new content
1216
  // overflowing its reference element
1217
  // To avoid this problem, we provide two options (x and y), which allow
1218
  // the consumer to define the offset origin.
1219
  // If we position a popper on top of a reference element, we can set
1220
  // `x` to `top` to make the popper grow towards its top instead of
1221
  // its bottom.
1222
  let left, top;
1223
  if (sideA === 'bottom') {
1224
    // when offsetParent is <html> the positioning is relative to the bottom of the screen (excluding the scrollbar)
1225
    // and not the bottom of the html element
1226
    if (offsetParent.nodeName === 'HTML') {
1227
      top = -offsetParent.clientHeight + offsets.bottom;
1228
    } else {
1229
      top = -offsetParentRect.height + offsets.bottom;
1230
    }
1231
  } else {
1232
    top = offsets.top;
1233
  }
1234
  if (sideB === 'right') {
1235
    if (offsetParent.nodeName === 'HTML') {
1236
      left = -offsetParent.clientWidth + offsets.right;
1237
    } else {
1238
      left = -offsetParentRect.width + offsets.right;
1239
    }
1240
  } else {
1241
    left = offsets.left;
1242
  }
1243
  if (gpuAcceleration && prefixedProperty) {
1244
    styles[prefixedProperty] = `translate3d(${left}px, ${top}px, 0)`;
1245
    styles[sideA] = 0;
1246
    styles[sideB] = 0;
1247
    styles.willChange = 'transform';
1248
  } else {
1249
    // othwerise, we use the standard `top`, `left`, `bottom` and `right` properties
1250
    const invertTop = sideA === 'bottom' ? -1 : 1;
1251
    const invertLeft = sideB === 'right' ? -1 : 1;
1252
    styles[sideA] = top * invertTop;
1253
    styles[sideB] = left * invertLeft;
1254
    styles.willChange = `${sideA}, ${sideB}`;
1255
  }
1256

1257
  // Attributes
1258
  const attributes = {
1259
    'x-placement': data.placement
1260
  };
1261

1262
  // Update `data` attributes, styles and arrowStyles
1263
  data.attributes = _extends({}, attributes, data.attributes);
1264
  data.styles = _extends({}, styles, data.styles);
1265
  data.arrowStyles = _extends({}, data.offsets.arrow, data.arrowStyles);
1266

1267
  return data;
1268
}
1269

1270
/**
1271
 * Helper used to know if the given modifier depends from another one.<br />
1272
 * It checks if the needed modifier is listed and enabled.
1273
 * @method
1274
 * @memberof Popper.Utils
1275
 * @param {Array} modifiers - list of modifiers
1276
 * @param {String} requestingName - name of requesting modifier
1277
 * @param {String} requestedName - name of requested modifier
1278
 * @returns {Boolean}
1279
 */
1280
function isModifierRequired(modifiers, requestingName, requestedName) {
1281
  const requesting = find(modifiers, ({ name }) => name === requestingName);
1282

1283
  const isRequired = !!requesting && modifiers.some(modifier => {
1284
    return modifier.name === requestedName && modifier.enabled && modifier.order < requesting.order;
1285
  });
1286

1287
  if (!isRequired) {
1288
    const requesting = `\`${requestingName}\``;
1289
    const requested = `\`${requestedName}\``;
1290
    console.warn(`${requested} modifier is required by ${requesting} modifier in order to work, be sure to include it before ${requesting}!`);
1291
  }
1292
  return isRequired;
1293
}
1294

1295
/**
1296
 * @function
1297
 * @memberof Modifiers
1298
 * @argument {Object} data - The data object generated by update method
1299
 * @argument {Object} options - Modifiers configuration and options
1300
 * @returns {Object} The data object, properly modified
1301
 */
1302
function arrow(data, options) {
1303
  // arrow depends on keepTogether in order to work
1304
  if (!isModifierRequired(data.instance.modifiers, 'arrow', 'keepTogether')) {
1305
    return data;
1306
  }
1307

1308
  let arrowElement = options.element;
1309

1310
  // if arrowElement is a string, suppose it's a CSS selector
1311
  if (typeof arrowElement === 'string') {
1312
    arrowElement = data.instance.popper.querySelector(arrowElement);
1313

1314
    // if arrowElement is not found, don't run the modifier
1315
    if (!arrowElement) {
1316
      return data;
1317
    }
1318
  } else {
1319
    // if the arrowElement isn't a query selector we must check that the
1320
    // provided DOM node is child of its popper node
1321
    if (!data.instance.popper.contains(arrowElement)) {
1322
      console.warn('WARNING: `arrow.element` must be child of its popper element!');
1323
      return data;
1324
    }
1325
  }
1326

1327
  const placement = data.placement.split('-')[0];
1328
  const { popper, reference } = data.offsets;
1329
  const isVertical = ['left', 'right'].indexOf(placement) !== -1;
1330

1331
  const len = isVertical ? 'height' : 'width';
1332
  const sideCapitalized = isVertical ? 'Top' : 'Left';
1333
  const side = sideCapitalized.toLowerCase();
1334
  const altSide = isVertical ? 'left' : 'top';
1335
  const opSide = isVertical ? 'bottom' : 'right';
1336
  const arrowElementSize = getOuterSizes(arrowElement)[len];
1337

1338
  //
1339
  // extends keepTogether behavior making sure the popper and its
1340
  // reference have enough pixels in conjunction
1341
  //
1342

1343
  // top/left side
1344
  if (reference[opSide] - arrowElementSize < popper[side]) {
1345
    data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowElementSize);
1346
  }
1347
  // bottom/right side
1348
  if (reference[side] + arrowElementSize > popper[opSide]) {
1349
    data.offsets.popper[side] += reference[side] + arrowElementSize - popper[opSide];
1350
  }
1351
  data.offsets.popper = getClientRect(data.offsets.popper);
1352

1353
  // compute center of the popper
1354
  const center = reference[side] + reference[len] / 2 - arrowElementSize / 2;
1355

1356
  // Compute the sideValue using the updated popper offsets
1357
  // take popper margin in account because we don't have this info available
1358
  const css = getStyleComputedProperty(data.instance.popper);
1359
  const popperMarginSide = parseFloat(css[`margin${sideCapitalized}`]);
1360
  const popperBorderSide = parseFloat(css[`border${sideCapitalized}Width`]);
1361
  let sideValue = center - data.offsets.popper[side] - popperMarginSide - popperBorderSide;
1362

1363
  // prevent arrowElement from being placed not contiguously to its popper
1364
  sideValue = Math.max(Math.min(popper[len] - arrowElementSize, sideValue), 0);
1365

1366
  data.arrowElement = arrowElement;
1367
  data.offsets.arrow = {
1368
    [side]: Math.round(sideValue),
1369
    [altSide]: '' // make sure to unset any eventual altSide value from the DOM node
1370
  };
1371

1372
  return data;
1373
}
1374

1375
/**
1376
 * Get the opposite placement variation of the given one
1377
 * @method
1378
 * @memberof Popper.Utils
1379
 * @argument {String} placement variation
1380
 * @returns {String} flipped placement variation
1381
 */
1382
function getOppositeVariation(variation) {
1383
  if (variation === 'end') {
1384
    return 'start';
1385
  } else if (variation === 'start') {
1386
    return 'end';
1387
  }
1388
  return variation;
1389
}
1390

1391
/**
1392
 * List of accepted placements to use as values of the `placement` option.<br />
1393
 * Valid placements are:
1394
 * - `auto`
1395
 * - `top`
1396
 * - `right`
1397
 * - `bottom`
1398
 * - `left`
1399
 *
1400
 * Each placement can have a variation from this list:
1401
 * - `-start`
1402
 * - `-end`
1403
 *
1404
 * Variations are interpreted easily if you think of them as the left to right
1405
 * written languages. Horizontally (`top` and `bottom`), `start` is left and `end`
1406
 * is right.<br />
1407
 * Vertically (`left` and `right`), `start` is top and `end` is bottom.
1408
 *
1409
 * Some valid examples are:
1410
 * - `top-end` (on top of reference, right aligned)
1411
 * - `right-start` (on right of reference, top aligned)
1412
 * - `bottom` (on bottom, centered)
1413
 * - `auto-end` (on the side with more space available, alignment depends by placement)
1414
 *
1415
 * @static
1416
 * @type {Array}
1417
 * @enum {String}
1418
 * @readonly
1419
 * @method placements
1420
 * @memberof Popper
1421
 */
1422
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'];
1423

1424
// Get rid of `auto` `auto-start` and `auto-end`
1425
const validPlacements = placements.slice(3);
1426

1427
/**
1428
 * Given an initial placement, returns all the subsequent placements
1429
 * clockwise (or counter-clockwise).
1430
 *
1431
 * @method
1432
 * @memberof Popper.Utils
1433
 * @argument {String} placement - A valid placement (it accepts variations)
1434
 * @argument {Boolean} counter - Set to true to walk the placements counterclockwise
1435
 * @returns {Array} placements including their variations
1436
 */
1437
function clockwise(placement, counter = false) {
1438
  const index = validPlacements.indexOf(placement);
1439
  const arr = validPlacements.slice(index + 1).concat(validPlacements.slice(0, index));
1440
  return counter ? arr.reverse() : arr;
1441
}
1442

1443
const BEHAVIORS = {
1444
  FLIP: 'flip',
1445
  CLOCKWISE: 'clockwise',
1446
  COUNTERCLOCKWISE: 'counterclockwise'
1447
};
1448

1449
/**
1450
 * @function
1451
 * @memberof Modifiers
1452
 * @argument {Object} data - The data object generated by update method
1453
 * @argument {Object} options - Modifiers configuration and options
1454
 * @returns {Object} The data object, properly modified
1455
 */
1456
function flip(data, options) {
1457
  // if `inner` modifier is enabled, we can't use the `flip` modifier
1458
  if (isModifierEnabled(data.instance.modifiers, 'inner')) {
1459
    return data;
1460
  }
1461

1462
  if (data.flipped && data.placement === data.originalPlacement) {
1463
    // seems like flip is trying to loop, probably there's not enough space on any of the flippable sides
1464
    return data;
1465
  }
1466

1467
  const boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, options.boundariesElement, data.positionFixed);
1468

1469
  let placement = data.placement.split('-')[0];
1470
  let placementOpposite = getOppositePlacement(placement);
1471
  let variation = data.placement.split('-')[1] || '';
1472

1473
  let flipOrder = [];
1474

1475
  switch (options.behavior) {
1476
    case BEHAVIORS.FLIP:
1477
      flipOrder = [placement, placementOpposite];
1478
      break;
1479
    case BEHAVIORS.CLOCKWISE:
1480
      flipOrder = clockwise(placement);
1481
      break;
1482
    case BEHAVIORS.COUNTERCLOCKWISE:
1483
      flipOrder = clockwise(placement, true);
1484
      break;
1485
    default:
1486
      flipOrder = options.behavior;
1487
  }
1488

1489
  flipOrder.forEach((step, index) => {
1490
    if (placement !== step || flipOrder.length === index + 1) {
1491
      return data;
1492
    }
1493

1494
    placement = data.placement.split('-')[0];
1495
    placementOpposite = getOppositePlacement(placement);
1496

1497
    const popperOffsets = data.offsets.popper;
1498
    const refOffsets = data.offsets.reference;
1499

1500
    // using floor because the reference offsets may contain decimals we are not going to consider here
1501
    const floor = Math.floor;
1502
    const 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);
1503

1504
    const overflowsLeft = floor(popperOffsets.left) < floor(boundaries.left);
1505
    const overflowsRight = floor(popperOffsets.right) > floor(boundaries.right);
1506
    const overflowsTop = floor(popperOffsets.top) < floor(boundaries.top);
1507
    const overflowsBottom = floor(popperOffsets.bottom) > floor(boundaries.bottom);
1508

1509
    const overflowsBoundaries = placement === 'left' && overflowsLeft || placement === 'right' && overflowsRight || placement === 'top' && overflowsTop || placement === 'bottom' && overflowsBottom;
1510

1511
    // flip the variation if required
1512
    const isVertical = ['top', 'bottom'].indexOf(placement) !== -1;
1513

1514
    // flips variation if reference element overflows boundaries
1515
    const flippedVariationByRef = !!options.flipVariations && (isVertical && variation === 'start' && overflowsLeft || isVertical && variation === 'end' && overflowsRight || !isVertical && variation === 'start' && overflowsTop || !isVertical && variation === 'end' && overflowsBottom);
1516

1517
    // flips variation if popper content overflows boundaries
1518
    const flippedVariationByContent = !!options.flipVariationsByContent && (isVertical && variation === 'start' && overflowsRight || isVertical && variation === 'end' && overflowsLeft || !isVertical && variation === 'start' && overflowsBottom || !isVertical && variation === 'end' && overflowsTop);
1519

1520
    const flippedVariation = flippedVariationByRef || flippedVariationByContent;
1521

1522
    if (overlapsRef || overflowsBoundaries || flippedVariation) {
1523
      // this boolean to detect any flip loop
1524
      data.flipped = true;
1525

1526
      if (overlapsRef || overflowsBoundaries) {
1527
        placement = flipOrder[index + 1];
1528
      }
1529

1530
      if (flippedVariation) {
1531
        variation = getOppositeVariation(variation);
1532
      }
1533

1534
      data.placement = placement + (variation ? '-' + variation : '');
1535

1536
      // this object contains `position`, we want to preserve it along with
1537
      // any additional property we may add in the future
1538
      data.offsets.popper = _extends({}, data.offsets.popper, getPopperOffsets(data.instance.popper, data.offsets.reference, data.placement));
1539

1540
      data = runModifiers(data.instance.modifiers, data, 'flip');
1541
    }
1542
  });
1543
  return data;
1544
}
1545

1546
/**
1547
 * @function
1548
 * @memberof Modifiers
1549
 * @argument {Object} data - The data object generated by update method
1550
 * @argument {Object} options - Modifiers configuration and options
1551
 * @returns {Object} The data object, properly modified
1552
 */
1553
function keepTogether(data) {
1554
  const { popper, reference } = data.offsets;
1555
  const placement = data.placement.split('-')[0];
1556
  const floor = Math.floor;
1557
  const isVertical = ['top', 'bottom'].indexOf(placement) !== -1;
1558
  const side = isVertical ? 'right' : 'bottom';
1559
  const opSide = isVertical ? 'left' : 'top';
1560
  const measurement = isVertical ? 'width' : 'height';
1561

1562
  if (popper[side] < floor(reference[opSide])) {
1563
    data.offsets.popper[opSide] = floor(reference[opSide]) - popper[measurement];
1564
  }
1565
  if (popper[opSide] > floor(reference[side])) {
1566
    data.offsets.popper[opSide] = floor(reference[side]);
1567
  }
1568

1569
  return data;
1570
}
1571

1572
/**
1573
 * Converts a string containing value + unit into a px value number
1574
 * @function
1575
 * @memberof {modifiers~offset}
1576
 * @private
1577
 * @argument {String} str - Value + unit string
1578
 * @argument {String} measurement - `height` or `width`
1579
 * @argument {Object} popperOffsets
1580
 * @argument {Object} referenceOffsets
1581
 * @returns {Number|String}
1582
 * Value in pixels, or original string if no values were extracted
1583
 */
1584
function toValue(str, measurement, popperOffsets, referenceOffsets) {
1585
  // separate value from unit
1586
  const split = str.match(/((?:\-|\+)?\d*\.?\d*)(.*)/);
1587
  const value = +split[1];
1588
  const unit = split[2];
1589

1590
  // If it's not a number it's an operator, I guess
1591
  if (!value) {
1592
    return str;
1593
  }
1594

1595
  if (unit.indexOf('%') === 0) {
1596
    let element;
1597
    switch (unit) {
1598
      case '%p':
1599
        element = popperOffsets;
1600
        break;
1601
      case '%':
1602
      case '%r':
1603
      default:
1604
        element = referenceOffsets;
1605
    }
1606

1607
    const rect = getClientRect(element);
1608
    return rect[measurement] / 100 * value;
1609
  } else if (unit === 'vh' || unit === 'vw') {
1610
    // if is a vh or vw, we calculate the size based on the viewport
1611
    let size;
1612
    if (unit === 'vh') {
1613
      size = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
1614
    } else {
1615
      size = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
1616
    }
1617
    return size / 100 * value;
1618
  } else {
1619
    // if is an explicit pixel unit, we get rid of the unit and keep the value
1620
    // if is an implicit unit, it's px, and we return just the value
1621
    return value;
1622
  }
1623
}
1624

1625
/**
1626
 * Parse an `offset` string to extrapolate `x` and `y` numeric offsets.
1627
 * @function
1628
 * @memberof {modifiers~offset}
1629
 * @private
1630
 * @argument {String} offset
1631
 * @argument {Object} popperOffsets
1632
 * @argument {Object} referenceOffsets
1633
 * @argument {String} basePlacement
1634
 * @returns {Array} a two cells array with x and y offsets in numbers
1635
 */
1636
function parseOffset(offset, popperOffsets, referenceOffsets, basePlacement) {
1637
  const offsets = [0, 0];
1638

1639
  // Use height if placement is left or right and index is 0 otherwise use width
1640
  // in this way the first offset will use an axis and the second one
1641
  // will use the other one
1642
  const useHeight = ['right', 'left'].indexOf(basePlacement) !== -1;
1643

1644
  // Split the offset string to obtain a list of values and operands
1645
  // The regex addresses values with the plus or minus sign in front (+10, -20, etc)
1646
  const fragments = offset.split(/(\+|\-)/).map(frag => frag.trim());
1647

1648
  // Detect if the offset string contains a pair of values or a single one
1649
  // they could be separated by comma or space
1650
  const divider = fragments.indexOf(find(fragments, frag => frag.search(/,|\s/) !== -1));
1651

1652
  if (fragments[divider] && fragments[divider].indexOf(',') === -1) {
1653
    console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.');
1654
  }
1655

1656
  // If divider is found, we divide the list of values and operands to divide
1657
  // them by ofset X and Y.
1658
  const splitRegex = /\s*,\s*|\s+/;
1659
  let ops = divider !== -1 ? [fragments.slice(0, divider).concat([fragments[divider].split(splitRegex)[0]]), [fragments[divider].split(splitRegex)[1]].concat(fragments.slice(divider + 1))] : [fragments];
1660

1661
  // Convert the values with units to absolute pixels to allow our computations
1662
  ops = ops.map((op, index) => {
1663
    // Most of the units rely on the orientation of the popper
1664
    const measurement = (index === 1 ? !useHeight : useHeight) ? 'height' : 'width';
1665
    let mergeWithPrevious = false;
1666
    return op
1667
    // This aggregates any `+` or `-` sign that aren't considered operators
1668
    // e.g.: 10 + +5 => [10, +, +5]
1669
    .reduce((a, b) => {
1670
      if (a[a.length - 1] === '' && ['+', '-'].indexOf(b) !== -1) {
1671
        a[a.length - 1] = b;
1672
        mergeWithPrevious = true;
1673
        return a;
1674
      } else if (mergeWithPrevious) {
1675
        a[a.length - 1] += b;
1676
        mergeWithPrevious = false;
1677
        return a;
1678
      } else {
1679
        return a.concat(b);
1680
      }
1681
    }, [])
1682
    // Here we convert the string values into number values (in px)
1683
    .map(str => toValue(str, measurement, popperOffsets, referenceOffsets));
1684
  });
1685

1686
  // Loop trough the offsets arrays and execute the operations
1687
  ops.forEach((op, index) => {
1688
    op.forEach((frag, index2) => {
1689
      if (isNumeric(frag)) {
1690
        offsets[index] += frag * (op[index2 - 1] === '-' ? -1 : 1);
1691
      }
1692
    });
1693
  });
1694
  return offsets;
1695
}
1696

1697
/**
1698
 * @function
1699
 * @memberof Modifiers
1700
 * @argument {Object} data - The data object generated by update method
1701
 * @argument {Object} options - Modifiers configuration and options
1702
 * @argument {Number|String} options.offset=0
1703
 * The offset value as described in the modifier description
1704
 * @returns {Object} The data object, properly modified
1705
 */
1706
function offset(data, { offset }) {
1707
  const { placement, offsets: { popper, reference } } = data;
1708
  const basePlacement = placement.split('-')[0];
1709

1710
  let offsets;
1711
  if (isNumeric(+offset)) {
1712
    offsets = [+offset, 0];
1713
  } else {
1714
    offsets = parseOffset(offset, popper, reference, basePlacement);
1715
  }
1716

1717
  if (basePlacement === 'left') {
1718
    popper.top += offsets[0];
1719
    popper.left -= offsets[1];
1720
  } else if (basePlacement === 'right') {
1721
    popper.top += offsets[0];
1722
    popper.left += offsets[1];
1723
  } else if (basePlacement === 'top') {
1724
    popper.left += offsets[0];
1725
    popper.top -= offsets[1];
1726
  } else if (basePlacement === 'bottom') {
1727
    popper.left += offsets[0];
1728
    popper.top += offsets[1];
1729
  }
1730

1731
  data.popper = popper;
1732
  return data;
1733
}
1734

1735
/**
1736
 * @function
1737
 * @memberof Modifiers
1738
 * @argument {Object} data - The data object generated by `update` method
1739
 * @argument {Object} options - Modifiers configuration and options
1740
 * @returns {Object} The data object, properly modified
1741
 */
1742
function preventOverflow(data, options) {
1743
  let boundariesElement = options.boundariesElement || getOffsetParent(data.instance.popper);
1744

1745
  // If offsetParent is the reference element, we really want to
1746
  // go one step up and use the next offsetParent as reference to
1747
  // avoid to make this modifier completely useless and look like broken
1748
  if (data.instance.reference === boundariesElement) {
1749
    boundariesElement = getOffsetParent(boundariesElement);
1750
  }
1751

1752
  // NOTE: DOM access here
1753
  // resets the popper's position so that the document size can be calculated excluding
1754
  // the size of the popper element itself
1755
  const transformProp = getSupportedPropertyName('transform');
1756
  const popperStyles = data.instance.popper.style; // assignment to help minification
1757
  const { top, left, [transformProp]: transform } = popperStyles;
1758
  popperStyles.top = '';
1759
  popperStyles.left = '';
1760
  popperStyles[transformProp] = '';
1761

1762
  const boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, boundariesElement, data.positionFixed);
1763

1764
  // NOTE: DOM access here
1765
  // restores the original style properties after the offsets have been computed
1766
  popperStyles.top = top;
1767
  popperStyles.left = left;
1768
  popperStyles[transformProp] = transform;
1769

1770
  options.boundaries = boundaries;
1771

1772
  const order = options.priority;
1773
  let popper = data.offsets.popper;
1774

1775
  const check = {
1776
    primary(placement) {
1777
      let value = popper[placement];
1778
      if (popper[placement] < boundaries[placement] && !options.escapeWithReference) {
1779
        value = Math.max(popper[placement], boundaries[placement]);
1780
      }
1781
      return { [placement]: value };
1782
    },
1783
    secondary(placement) {
1784
      const mainSide = placement === 'right' ? 'left' : 'top';
1785
      let value = popper[mainSide];
1786
      if (popper[placement] > boundaries[placement] && !options.escapeWithReference) {
1787
        value = Math.min(popper[mainSide], boundaries[placement] - (placement === 'right' ? popper.width : popper.height));
1788
      }
1789
      return { [mainSide]: value };
1790
    }
1791
  };
1792

1793
  order.forEach(placement => {
1794
    const side = ['left', 'top'].indexOf(placement) !== -1 ? 'primary' : 'secondary';
1795
    popper = _extends({}, popper, check[side](placement));
1796
  });
1797

1798
  data.offsets.popper = popper;
1799

1800
  return data;
1801
}
1802

1803
/**
1804
 * @function
1805
 * @memberof Modifiers
1806
 * @argument {Object} data - The data object generated by `update` method
1807
 * @argument {Object} options - Modifiers configuration and options
1808
 * @returns {Object} The data object, properly modified
1809
 */
1810
function shift(data) {
1811
  const placement = data.placement;
1812
  const basePlacement = placement.split('-')[0];
1813
  const shiftvariation = placement.split('-')[1];
1814

1815
  // if shift shiftvariation is specified, run the modifier
1816
  if (shiftvariation) {
1817
    const { reference, popper } = data.offsets;
1818
    const isVertical = ['bottom', 'top'].indexOf(basePlacement) !== -1;
1819
    const side = isVertical ? 'left' : 'top';
1820
    const measurement = isVertical ? 'width' : 'height';
1821

1822
    const shiftOffsets = {
1823
      start: { [side]: reference[side] },
1824
      end: {
1825
        [side]: reference[side] + reference[measurement] - popper[measurement]
1826
      }
1827
    };
1828

1829
    data.offsets.popper = _extends({}, popper, shiftOffsets[shiftvariation]);
1830
  }
1831

1832
  return data;
1833
}
1834

1835
/**
1836
 * @function
1837
 * @memberof Modifiers
1838
 * @argument {Object} data - The data object generated by update method
1839
 * @argument {Object} options - Modifiers configuration and options
1840
 * @returns {Object} The data object, properly modified
1841
 */
1842
function hide(data) {
1843
  if (!isModifierRequired(data.instance.modifiers, 'hide', 'preventOverflow')) {
1844
    return data;
1845
  }
1846

1847
  const refRect = data.offsets.reference;
1848
  const bound = find(data.instance.modifiers, modifier => modifier.name === 'preventOverflow').boundaries;
1849

1850
  if (refRect.bottom < bound.top || refRect.left > bound.right || refRect.top > bound.bottom || refRect.right < bound.left) {
1851
    // Avoid unnecessary DOM access if visibility hasn't changed
1852
    if (data.hide === true) {
1853
      return data;
1854
    }
1855

1856
    data.hide = true;
1857
    data.attributes['x-out-of-boundaries'] = '';
1858
  } else {
1859
    // Avoid unnecessary DOM access if visibility hasn't changed
1860
    if (data.hide === false) {
1861
      return data;
1862
    }
1863

1864
    data.hide = false;
1865
    data.attributes['x-out-of-boundaries'] = false;
1866
  }
1867

1868
  return data;
1869
}
1870

1871
/**
1872
 * @function
1873
 * @memberof Modifiers
1874
 * @argument {Object} data - The data object generated by `update` method
1875
 * @argument {Object} options - Modifiers configuration and options
1876
 * @returns {Object} The data object, properly modified
1877
 */
1878
function inner(data) {
1879
  const placement = data.placement;
1880
  const basePlacement = placement.split('-')[0];
1881
  const { popper, reference } = data.offsets;
1882
  const isHoriz = ['left', 'right'].indexOf(basePlacement) !== -1;
1883

1884
  const subtractLength = ['top', 'left'].indexOf(basePlacement) === -1;
1885

1886
  popper[isHoriz ? 'left' : 'top'] = reference[basePlacement] - (subtractLength ? popper[isHoriz ? 'width' : 'height'] : 0);
1887

1888
  data.placement = getOppositePlacement(placement);
1889
  data.offsets.popper = getClientRect(popper);
1890

1891
  return data;
1892
}
1893

1894
/**
1895
 * Modifier function, each modifier can have a function of this type assigned
1896
 * to its `fn` property.<br />
1897
 * These functions will be called on each update, this means that you must
1898
 * make sure they are performant enough to avoid performance bottlenecks.
1899
 *
1900
 * @function ModifierFn
1901
 * @argument {dataObject} data - The data object generated by `update` method
1902
 * @argument {Object} options - Modifiers configuration and options
1903
 * @returns {dataObject} The data object, properly modified
1904
 */
1905

1906
/**
1907
 * Modifiers are plugins used to alter the behavior of your poppers.<br />
1908
 * Popper.js uses a set of 9 modifiers to provide all the basic functionalities
1909
 * needed by the library.
1910
 *
1911
 * Usually you don't want to override the `order`, `fn` and `onLoad` props.
1912
 * All the other properties are configurations that could be tweaked.
1913
 * @namespace modifiers
1914
 */
1915
var modifiers = {
1916
  /**
1917
   * Modifier used to shift the popper on the start or end of its reference
1918
   * element.<br />
1919
   * It will read the variation of the `placement` property.<br />
1920
   * It can be one either `-end` or `-start`.
1921
   * @memberof modifiers
1922
   * @inner
1923
   */
1924
  shift: {
1925
    /** @prop {number} order=100 - Index used to define the order of execution */
1926
    order: 100,
1927
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
1928
    enabled: true,
1929
    /** @prop {ModifierFn} */
1930
    fn: shift
1931
  },
1932

1933
  /**
1934
   * The `offset` modifier can shift your popper on both its axis.
1935
   *
1936
   * It accepts the following units:
1937
   * - `px` or unit-less, interpreted as pixels
1938
   * - `%` or `%r`, percentage relative to the length of the reference element
1939
   * - `%p`, percentage relative to the length of the popper element
1940
   * - `vw`, CSS viewport width unit
1941
   * - `vh`, CSS viewport height unit
1942
   *
1943
   * For length is intended the main axis relative to the placement of the popper.<br />
1944
   * This means that if the placement is `top` or `bottom`, the length will be the
1945
   * `width`. In case of `left` or `right`, it will be the `height`.
1946
   *
1947
   * You can provide a single value (as `Number` or `String`), or a pair of values
1948
   * as `String` divided by a comma or one (or more) white spaces.<br />
1949
   * The latter is a deprecated method because it leads to confusion and will be
1950
   * removed in v2.<br />
1951
   * Additionally, it accepts additions and subtractions between different units.
1952
   * Note that multiplications and divisions aren't supported.
1953
   *
1954
   * Valid examples are:
1955
   * ```
1956
   * 10
1957
   * '10%'
1958
   * '10, 10'
1959
   * '10%, 10'
1960
   * '10 + 10%'
1961
   * '10 - 5vh + 3%'
1962
   * '-10px + 5vh, 5px - 6%'
1963
   * ```
1964
   * > **NB**: If you desire to apply offsets to your poppers in a way that may make them overlap
1965
   * > with their reference element, unfortunately, you will have to disable the `flip` modifier.
1966
   * > You can read more on this at this [issue](https://github.com/FezVrasta/popper.js/issues/373).
1967
   *
1968
   * @memberof modifiers
1969
   * @inner
1970
   */
1971
  offset: {
1972
    /** @prop {number} order=200 - Index used to define the order of execution */
1973
    order: 200,
1974
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
1975
    enabled: true,
1976
    /** @prop {ModifierFn} */
1977
    fn: offset,
1978
    /** @prop {Number|String} offset=0
1979
     * The offset value as described in the modifier description
1980
     */
1981
    offset: 0
1982
  },
1983

1984
  /**
1985
   * Modifier used to prevent the popper from being positioned outside the boundary.
1986
   *
1987
   * A scenario exists where the reference itself is not within the boundaries.<br />
1988
   * We can say it has "escaped the boundaries" — or just "escaped".<br />
1989
   * In this case we need to decide whether the popper should either:
1990
   *
1991
   * - detach from the reference and remain "trapped" in the boundaries, or
1992
   * - if it should ignore the boundary and "escape with its reference"
1993
   *
1994
   * When `escapeWithReference` is set to`true` and reference is completely
1995
   * outside its boundaries, the popper will overflow (or completely leave)
1996
   * the boundaries in order to remain attached to the edge of the reference.
1997
   *
1998
   * @memberof modifiers
1999
   * @inner
2000
   */
2001
  preventOverflow: {
2002
    /** @prop {number} order=300 - Index used to define the order of execution */
2003
    order: 300,
2004
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
2005
    enabled: true,
2006
    /** @prop {ModifierFn} */
2007
    fn: preventOverflow,
2008
    /**
2009
     * @prop {Array} [priority=['left','right','top','bottom']]
2010
     * Popper will try to prevent overflow following these priorities by default,
2011
     * then, it could overflow on the left and on top of the `boundariesElement`
2012
     */
2013
    priority: ['left', 'right', 'top', 'bottom'],
2014
    /**
2015
     * @prop {number} padding=5
2016
     * Amount of pixel used to define a minimum distance between the boundaries
2017
     * and the popper. This makes sure the popper always has a little padding
2018
     * between the edges of its container
2019
     */
2020
    padding: 5,
2021
    /**
2022
     * @prop {String|HTMLElement} boundariesElement='scrollParent'
2023
     * Boundaries used by the modifier. Can be `scrollParent`, `window`,
2024
     * `viewport` or any DOM element.
2025
     */
2026
    boundariesElement: 'scrollParent'
2027
  },
2028

2029
  /**
2030
   * Modifier used to make sure the reference and its popper stay near each other
2031
   * without leaving any gap between the two. Especially useful when the arrow is
2032
   * enabled and you want to ensure that it points to its reference element.
2033
   * It cares only about the first axis. You can still have poppers with margin
2034
   * between the popper and its reference element.
2035
   * @memberof modifiers
2036
   * @inner
2037
   */
2038
  keepTogether: {
2039
    /** @prop {number} order=400 - Index used to define the order of execution */
2040
    order: 400,
2041
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
2042
    enabled: true,
2043
    /** @prop {ModifierFn} */
2044
    fn: keepTogether
2045
  },
2046

2047
  /**
2048
   * This modifier is used to move the `arrowElement` of the popper to make
2049
   * sure it is positioned between the reference element and its popper element.
2050
   * It will read the outer size of the `arrowElement` node to detect how many
2051
   * pixels of conjunction are needed.
2052
   *
2053
   * It has no effect if no `arrowElement` is provided.
2054
   * @memberof modifiers
2055
   * @inner
2056
   */
2057
  arrow: {
2058
    /** @prop {number} order=500 - Index used to define the order of execution */
2059
    order: 500,
2060
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
2061
    enabled: true,
2062
    /** @prop {ModifierFn} */
2063
    fn: arrow,
2064
    /** @prop {String|HTMLElement} element='[x-arrow]' - Selector or node used as arrow */
2065
    element: '[x-arrow]'
2066
  },
2067

2068
  /**
2069
   * Modifier used to flip the popper's placement when it starts to overlap its
2070
   * reference element.
2071
   *
2072
   * Requires the `preventOverflow` modifier before it in order to work.
2073
   *
2074
   * **NOTE:** this modifier will interrupt the current update cycle and will
2075
   * restart it if it detects the need to flip the placement.
2076
   * @memberof modifiers
2077
   * @inner
2078
   */
2079
  flip: {
2080
    /** @prop {number} order=600 - Index used to define the order of execution */
2081
    order: 600,
2082
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
2083
    enabled: true,
2084
    /** @prop {ModifierFn} */
2085
    fn: flip,
2086
    /**
2087
     * @prop {String|Array} behavior='flip'
2088
     * The behavior used to change the popper's placement. It can be one of
2089
     * `flip`, `clockwise`, `counterclockwise` or an array with a list of valid
2090
     * placements (with optional variations)
2091
     */
2092
    behavior: 'flip',
2093
    /**
2094
     * @prop {number} padding=5
2095
     * The popper will flip if it hits the edges of the `boundariesElement`
2096
     */
2097
    padding: 5,
2098
    /**
2099
     * @prop {String|HTMLElement} boundariesElement='viewport'
2100
     * The element which will define the boundaries of the popper position.
2101
     * The popper will never be placed outside of the defined boundaries
2102
     * (except if `keepTogether` is enabled)
2103
     */
2104
    boundariesElement: 'viewport',
2105
    /**
2106
     * @prop {Boolean} flipVariations=false
2107
     * The popper will switch placement variation between `-start` and `-end` when
2108
     * the reference element overlaps its boundaries.
2109
     *
2110
     * The original placement should have a set variation.
2111
     */
2112
    flipVariations: false,
2113
    /**
2114
     * @prop {Boolean} flipVariationsByContent=false
2115
     * The popper will switch placement variation between `-start` and `-end` when
2116
     * the popper element overlaps its reference boundaries.
2117
     *
2118
     * The original placement should have a set variation.
2119
     */
2120
    flipVariationsByContent: false
2121
  },
2122

2123
  /**
2124
   * Modifier used to make the popper flow toward the inner of the reference element.
2125
   * By default, when this modifier is disabled, the popper will be placed outside
2126
   * the reference element.
2127
   * @memberof modifiers
2128
   * @inner
2129
   */
2130
  inner: {
2131
    /** @prop {number} order=700 - Index used to define the order of execution */
2132
    order: 700,
2133
    /** @prop {Boolean} enabled=false - Whether the modifier is enabled or not */
2134
    enabled: false,
2135
    /** @prop {ModifierFn} */
2136
    fn: inner
2137
  },
2138

2139
  /**
2140
   * Modifier used to hide the popper when its reference element is outside of the
2141
   * popper boundaries. It will set a `x-out-of-boundaries` attribute which can
2142
   * be used to hide with a CSS selector the popper when its reference is
2143
   * out of boundaries.
2144
   *
2145
   * Requires the `preventOverflow` modifier before it in order to work.
2146
   * @memberof modifiers
2147
   * @inner
2148
   */
2149
  hide: {
2150
    /** @prop {number} order=800 - Index used to define the order of execution */
2151
    order: 800,
2152
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
2153
    enabled: true,
2154
    /** @prop {ModifierFn} */
2155
    fn: hide
2156
  },
2157

2158
  /**
2159
   * Computes the style that will be applied to the popper element to gets
2160
   * properly positioned.
2161
   *
2162
   * Note that this modifier will not touch the DOM, it just prepares the styles
2163
   * so that `applyStyle` modifier can apply it. This separation is useful
2164
   * in case you need to replace `applyStyle` with a custom implementation.
2165
   *
2166
   * This modifier has `850` as `order` value to maintain backward compatibility
2167
   * with previous versions of Popper.js. Expect the modifiers ordering method
2168
   * to change in future major versions of the library.
2169
   *
2170
   * @memberof modifiers
2171
   * @inner
2172
   */
2173
  computeStyle: {
2174
    /** @prop {number} order=850 - Index used to define the order of execution */
2175
    order: 850,
2176
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
2177
    enabled: true,
2178
    /** @prop {ModifierFn} */
2179
    fn: computeStyle,
2180
    /**
2181
     * @prop {Boolean} gpuAcceleration=true
2182
     * If true, it uses the CSS 3D transformation to position the popper.
2183
     * Otherwise, it will use the `top` and `left` properties
2184
     */
2185
    gpuAcceleration: true,
2186
    /**
2187
     * @prop {string} [x='bottom']
2188
     * Where to anchor the X axis (`bottom` or `top`). AKA X offset origin.
2189
     * Change this if your popper should grow in a direction different from `bottom`
2190
     */
2191
    x: 'bottom',
2192
    /**
2193
     * @prop {string} [x='left']
2194
     * Where to anchor the Y axis (`left` or `right`). AKA Y offset origin.
2195
     * Change this if your popper should grow in a direction different from `right`
2196
     */
2197
    y: 'right'
2198
  },
2199

2200
  /**
2201
   * Applies the computed styles to the popper element.
2202
   *
2203
   * All the DOM manipulations are limited to this modifier. This is useful in case
2204
   * you want to integrate Popper.js inside a framework or view library and you
2205
   * want to delegate all the DOM manipulations to it.
2206
   *
2207
   * Note that if you disable this modifier, you must make sure the popper element
2208
   * has its position set to `absolute` before Popper.js can do its work!
2209
   *
2210
   * Just disable this modifier and define your own to achieve the desired effect.
2211
   *
2212
   * @memberof modifiers
2213
   * @inner
2214
   */
2215
  applyStyle: {
2216
    /** @prop {number} order=900 - Index used to define the order of execution */
2217
    order: 900,
2218
    /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
2219
    enabled: true,
2220
    /** @prop {ModifierFn} */
2221
    fn: applyStyle,
2222
    /** @prop {Function} */
2223
    onLoad: applyStyleOnLoad,
2224
    /**
2225
     * @deprecated since version 1.10.0, the property moved to `computeStyle` modifier
2226
     * @prop {Boolean} gpuAcceleration=true
2227
     * If true, it uses the CSS 3D transformation to position the popper.
2228
     * Otherwise, it will use the `top` and `left` properties
2229
     */
2230
    gpuAcceleration: undefined
2231
  }
2232
};
2233

2234
/**
2235
 * The `dataObject` is an object containing all the information used by Popper.js.
2236
 * This object is passed to modifiers and to the `onCreate` and `onUpdate` callbacks.
2237
 * @name dataObject
2238
 * @property {Object} data.instance The Popper.js instance
2239
 * @property {String} data.placement Placement applied to popper
2240
 * @property {String} data.originalPlacement Placement originally defined on init
2241
 * @property {Boolean} data.flipped True if popper has been flipped by flip modifier
2242
 * @property {Boolean} data.hide True if the reference element is out of boundaries, useful to know when to hide the popper
2243
 * @property {HTMLElement} data.arrowElement Node used as arrow by arrow modifier
2244
 * @property {Object} data.styles Any CSS property defined here will be applied to the popper. It expects the JavaScript nomenclature (eg. `marginBottom`)
2245
 * @property {Object} data.arrowStyles Any CSS property defined here will be applied to the popper arrow. It expects the JavaScript nomenclature (eg. `marginBottom`)
2246
 * @property {Object} data.boundaries Offsets of the popper boundaries
2247
 * @property {Object} data.offsets The measurements of popper, reference and arrow elements
2248
 * @property {Object} data.offsets.popper `top`, `left`, `width`, `height` values
2249
 * @property {Object} data.offsets.reference `top`, `left`, `width`, `height` values
2250
 * @property {Object} data.offsets.arrow] `top` and `left` offsets, only one of them will be different from 0
2251
 */
2252

2253
/**
2254
 * Default options provided to Popper.js constructor.<br />
2255
 * These can be overridden using the `options` argument of Popper.js.<br />
2256
 * To override an option, simply pass an object with the same
2257
 * structure of the `options` object, as the 3rd argument. For example:
2258
 * ```
2259
 * new Popper(ref, pop, {
2260
 *   modifiers: {
2261
 *     preventOverflow: { enabled: false }
2262
 *   }
2263
 * })
2264
 * ```
2265
 * @type {Object}
2266
 * @static
2267
 * @memberof Popper
2268
 */
2269
var Defaults = {
2270
  /**
2271
   * Popper's placement.
2272
   * @prop {Popper.placements} placement='bottom'
2273
   */
2274
  placement: 'bottom',
2275

2276
  /**
2277
   * Set this to true if you want popper to position it self in 'fixed' mode
2278
   * @prop {Boolean} positionFixed=false
2279
   */
2280
  positionFixed: false,
2281

2282
  /**
2283
   * Whether events (resize, scroll) are initially enabled.
2284
   * @prop {Boolean} eventsEnabled=true
2285
   */
2286
  eventsEnabled: true,
2287

2288
  /**
2289
   * Set to true if you want to automatically remove the popper when
2290
   * you call the `destroy` method.
2291
   * @prop {Boolean} removeOnDestroy=false
2292
   */
2293
  removeOnDestroy: false,
2294

2295
  /**
2296
   * Callback called when the popper is created.<br />
2297
   * By default, it is set to no-op.<br />
2298
   * Access Popper.js instance with `data.instance`.
2299
   * @prop {onCreate}
2300
   */
2301
  onCreate: () => {},
2302

2303
  /**
2304
   * Callback called when the popper is updated. This callback is not called
2305
   * on the initialization/creation of the popper, but only on subsequent
2306
   * updates.<br />
2307
   * By default, it is set to no-op.<br />
2308
   * Access Popper.js instance with `data.instance`.
2309
   * @prop {onUpdate}
2310
   */
2311
  onUpdate: () => {},
2312

2313
  /**
2314
   * List of modifiers used to modify the offsets before they are applied to the popper.
2315
   * They provide most of the functionalities of Popper.js.
2316
   * @prop {modifiers}
2317
   */
2318
  modifiers
2319
};
2320

2321
/**
2322
 * @callback onCreate
2323
 * @param {dataObject} data
2324
 */
2325

2326
/**
2327
 * @callback onUpdate
2328
 * @param {dataObject} data
2329
 */
2330

2331
// Utils
2332
// Methods
2333
class Popper {
2334
  /**
2335
   * Creates a new Popper.js instance.
2336
   * @class Popper
2337
   * @param {Element|referenceObject} reference - The reference element used to position the popper
2338
   * @param {Element} popper - The HTML / XML element used as the popper
2339
   * @param {Object} options - Your custom options to override the ones defined in [Defaults](#defaults)
2340
   * @return {Object} instance - The generated Popper.js instance
2341
   */
2342
  constructor(reference, popper, options = {}) {
2343
    this.scheduleUpdate = () => requestAnimationFrame(this.update);
2344

2345
    // make update() debounced, so that it only runs at most once-per-tick
2346
    this.update = debounce(this.update.bind(this));
2347

2348
    // with {} we create a new object with the options inside it
2349
    this.options = _extends({}, Popper.Defaults, options);
2350

2351
    // init state
2352
    this.state = {
2353
      isDestroyed: false,
2354
      isCreated: false,
2355
      scrollParents: []
2356
    };
2357

2358
    // get reference and popper elements (allow jQuery wrappers)
2359
    this.reference = reference && reference.jquery ? reference[0] : reference;
2360
    this.popper = popper && popper.jquery ? popper[0] : popper;
2361

2362
    // Deep merge modifiers options
2363
    this.options.modifiers = {};
2364
    Object.keys(_extends({}, Popper.Defaults.modifiers, options.modifiers)).forEach(name => {
2365
      this.options.modifiers[name] = _extends({}, Popper.Defaults.modifiers[name] || {}, options.modifiers ? options.modifiers[name] : {});
2366
    });
2367

2368
    // Refactoring modifiers' list (Object => Array)
2369
    this.modifiers = Object.keys(this.options.modifiers).map(name => _extends({
2370
      name
2371
    }, this.options.modifiers[name]))
2372
    // sort the modifiers by order
2373
    .sort((a, b) => a.order - b.order);
2374

2375
    // modifiers have the ability to execute arbitrary code when Popper.js get inited
2376
    // such code is executed in the same order of its modifier
2377
    // they could add new properties to their options configuration
2378
    // BE AWARE: don't add options to `options.modifiers.name` but to `modifierOptions`!
2379
    this.modifiers.forEach(modifierOptions => {
2380
      if (modifierOptions.enabled && isFunction(modifierOptions.onLoad)) {
2381
        modifierOptions.onLoad(this.reference, this.popper, this.options, modifierOptions, this.state);
2382
      }
2383
    });
2384

2385
    // fire the first update to position the popper in the right place
2386
    this.update();
2387

2388
    const eventsEnabled = this.options.eventsEnabled;
2389
    if (eventsEnabled) {
2390
      // setup event listeners, they will take care of update the position in specific situations
2391
      this.enableEventListeners();
2392
    }
2393

2394
    this.state.eventsEnabled = eventsEnabled;
2395
  }
2396

2397
  // We can't use class properties because they don't get listed in the
2398
  // class prototype and break stuff like Sinon stubs
2399
  update() {
2400
    return update.call(this);
2401
  }
2402
  destroy() {
2403
    return destroy.call(this);
2404
  }
2405
  enableEventListeners() {
2406
    return enableEventListeners.call(this);
2407
  }
2408
  disableEventListeners() {
2409
    return disableEventListeners.call(this);
2410
  }
2411

2412
  /**
2413
   * Schedules an update. It will run on the next UI update available.
2414
   * @method scheduleUpdate
2415
   * @memberof Popper
2416
   */
2417

2418

2419
  /**
2420
   * Collection of utilities useful when writing custom modifiers.
2421
   * Starting from version 1.7, this method is available only if you
2422
   * include `popper-utils.js` before `popper.js`.
2423
   *
2424
   * **DEPRECATION**: This way to access PopperUtils is deprecated
2425
   * and will be removed in v2! Use the PopperUtils module directly instead.
2426
   * Due to the high instability of the methods contained in Utils, we can't
2427
   * guarantee them to follow semver. Use them at your own risk!
2428
   * @static
2429
   * @private
2430
   * @type {Object}
2431
   * @deprecated since version 1.8
2432
   * @member Utils
2433
   * @memberof Popper
2434
   */
2435
}
2436

2437
/**
2438
 * The `referenceObject` is an object that provides an interface compatible with Popper.js
2439
 * and lets you use it as replacement of a real DOM node.<br />
2440
 * You can use this method to position a popper relatively to a set of coordinates
2441
 * in case you don't have a DOM node to use as reference.
2442
 *
2443
 * ```
2444
 * new Popper(referenceObject, popperNode);
2445
 * ```
2446
 *
2447
 * NB: This feature isn't supported in Internet Explorer 10.
2448
 * @name referenceObject
2449
 * @property {Function} data.getBoundingClientRect
2450
 * A function that returns a set of coordinates compatible with the native `getBoundingClientRect` method.
2451
 * @property {number} data.clientWidth
2452
 * An ES6 getter that will return the width of the virtual reference element.
2453
 * @property {number} data.clientHeight
2454
 * An ES6 getter that will return the height of the virtual reference element.
2455
 */
2456

2457
Popper.Utils = (typeof window !== 'undefined' ? window : global).PopperUtils;
2458
Popper.placements = placements;
2459
Popper.Defaults = Defaults;
2460

2461
export default Popper;
2462
//# sourceMappingURL=popper.js.map
2463

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.