UNPKG

103 kBJavaScriptView Raw
1/**!
2* Copyright 2017 atomiks (Tippy) & FezVrasta (Popper)
3* @file tippy.js (popper.js 1.8.5 included) | Pure JS Tooltip Library
4* @version 0.11.0
5* @license MIT
6*/
7
8(function (global, factory) {
9 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
10 typeof define === 'function' && define.amd ? define(factory) :
11 (global.Tippy = factory());
12}(this, (function () { 'use strict';
13
14var nativeHints = ['native code', '[object MutationObserverConstructor]'];
15
16/**
17 * Determine if a function is implemented natively (as opposed to a polyfill).
18 * @method
19 * @memberof Popper.Utils
20 * @argument {Function | undefined} fn the function to check
21 * @returns {boolean}
22 */
23var isNative = (function (fn) {
24 return nativeHints.some(function (hint) {
25 return (fn || '').toString().indexOf(hint) > -1;
26 });
27});
28
29var isBrowser = typeof window !== 'undefined';
30var longerTimeoutBrowsers = ['Edge', 'Trident', 'Firefox'];
31var timeoutDuration = 0;
32for (var i = 0; i < longerTimeoutBrowsers.length; i += 1) {
33 if (isBrowser && navigator.userAgent.indexOf(longerTimeoutBrowsers[i]) >= 0) {
34 timeoutDuration = 1;
35 break;
36 }
37}
38
39function microtaskDebounce(fn) {
40 var scheduled = false;
41 var i = 0;
42 var elem = document.createElement('span');
43
44 // MutationObserver provides a mechanism for scheduling microtasks, which
45 // are scheduled *before* the next task. This gives us a way to debounce
46 // a function but ensure it's called *before* the next paint.
47 var observer = new MutationObserver(function () {
48 fn();
49 scheduled = false;
50 });
51
52 observer.observe(elem, { attributes: true });
53
54 return function () {
55 if (!scheduled) {
56 scheduled = true;
57 elem.setAttribute('x-index', i);
58 i = i + 1; // don't use compund (+=) because it doesn't get optimized in V8
59 }
60 };
61}
62
63function taskDebounce(fn) {
64 var scheduled = false;
65 return function () {
66 if (!scheduled) {
67 scheduled = true;
68 setTimeout(function () {
69 scheduled = false;
70 fn();
71 }, timeoutDuration);
72 }
73 };
74}
75
76// It's common for MutationObserver polyfills to be seen in the wild, however
77// these rely on Mutation Events which only occur when an element is connected
78// to the DOM. The algorithm used in this module does not use a connected element,
79// and so we must ensure that a *native* MutationObserver is available.
80var supportsNativeMutationObserver = isBrowser && isNative(window.MutationObserver);
81
82/**
83* Create a debounced version of a method, that's asynchronously deferred
84* but called in the minimum time possible.
85*
86* @method
87* @memberof Popper.Utils
88* @argument {Function} fn
89* @returns {Function}
90*/
91var debounce = supportsNativeMutationObserver ? microtaskDebounce : taskDebounce;
92
93/**
94 * Tells if a given input is a number
95 * @method
96 * @memberof Popper.Utils
97 * @param {*} input to check
98 * @return {Boolean}
99 */
100function isNumeric(n) {
101 return n !== '' && !isNaN(parseFloat(n)) && isFinite(n);
102}
103
104/**
105 * Set the style to the given popper
106 * @method
107 * @memberof Popper.Utils
108 * @argument {Element} element - Element to apply the style to
109 * @argument {Object} styles - Object with a list of properties and values which will be applied to the element
110 */
111function setStyles(element, styles) {
112 Object.keys(styles).forEach(function (prop) {
113 var unit = '';
114 // add unit if the value is numeric and is one of the following
115 if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && isNumeric(styles[prop])) {
116 unit = 'px';
117 }
118 element.style[prop] = styles[prop] + unit;
119 });
120}
121
122/**
123 * Get the prefixed supported property name
124 * @method
125 * @memberof Popper.Utils
126 * @argument {String} property (camelCase)
127 * @returns {String} prefixed property (camelCase)
128 */
129function getSupportedPropertyName(property) {
130 var prefixes = [false, 'ms', 'webkit', 'moz', 'o'];
131 var upperProp = property.charAt(0).toUpperCase() + property.slice(1);
132
133 for (var i = 0; i < prefixes.length - 1; i++) {
134 var prefix = prefixes[i];
135 var toCheck = prefix ? '' + prefix + upperProp : property;
136 if (typeof window.document.body.style[toCheck] !== 'undefined') {
137 return toCheck;
138 }
139 }
140 return null;
141}
142
143function isOffsetContainer(element) {
144 var nodeName = element.nodeName;
145
146 if (nodeName === 'BODY') {
147 return false;
148 }
149 return nodeName === 'HTML' || element.firstElementChild.offsetParent === element;
150}
151
152/**
153 * Finds the root node (document, shadowDOM root) of the given element
154 * @method
155 * @memberof Popper.Utils
156 * @argument {Element} node
157 * @returns {Element} root node
158 */
159function getRoot(node) {
160 if (node.parentNode !== null) {
161 return getRoot(node.parentNode);
162 }
163
164 return node;
165}
166
167/**
168 * Returns the offset parent of the given element
169 * @method
170 * @memberof Popper.Utils
171 * @argument {Element} element
172 * @returns {Element} offset parent
173 */
174function getOffsetParent(element) {
175 // NOTE: 1 DOM access here
176 var offsetParent = element && element.offsetParent;
177 var nodeName = offsetParent && offsetParent.nodeName;
178
179 if (!nodeName || nodeName === 'BODY' || nodeName === 'HTML') {
180 return window.document.documentElement;
181 }
182
183 return offsetParent;
184}
185
186/**
187 * Finds the offset parent common to the two provided nodes
188 * @method
189 * @memberof Popper.Utils
190 * @argument {Element} element1
191 * @argument {Element} element2
192 * @returns {Element} common offset parent
193 */
194function findCommonOffsetParent(element1, element2) {
195 // This check is needed to avoid errors in case one of the elements isn't defined for any reason
196 if (!element1 || !element1.nodeType || !element2 || !element2.nodeType) {
197 return window.document.documentElement;
198 }
199
200 // Here we make sure to give as "start" the element that comes first in the DOM
201 var order = element1.compareDocumentPosition(element2) & Node.DOCUMENT_POSITION_FOLLOWING;
202 var start = order ? element1 : element2;
203 var end = order ? element2 : element1;
204
205 // Get common ancestor container
206 var range = document.createRange();
207 range.setStart(start, 0);
208 range.setEnd(end, 0);
209 var commonAncestorContainer = range.commonAncestorContainer;
210
211 // Both nodes are inside #document
212
213 if (element1 !== commonAncestorContainer && element2 !== commonAncestorContainer) {
214 if (isOffsetContainer(commonAncestorContainer)) {
215 return commonAncestorContainer;
216 }
217
218 return getOffsetParent(commonAncestorContainer);
219 }
220
221 // one of the nodes is inside shadowDOM, find which one
222 var element1root = getRoot(element1);
223 if (element1root.host) {
224 return findCommonOffsetParent(element1root.host, element2);
225 } else {
226 return findCommonOffsetParent(element1, getRoot(element2).host);
227 }
228}
229
230/**
231 * Get CSS computed property of the given element
232 * @method
233 * @memberof Popper.Utils
234 * @argument {Eement} element
235 * @argument {String} property
236 */
237function getStyleComputedProperty(element, property) {
238 if (element.nodeType !== 1) {
239 return [];
240 }
241 // NOTE: 1 DOM access here
242 var css = window.getComputedStyle(element, null);
243 return property ? css[property] : css;
244}
245
246/**
247 * Gets the scroll value of the given element in the given side (top and left)
248 * @method
249 * @memberof Popper.Utils
250 * @argument {Element} element
251 * @argument {String} side `top` or `left`
252 * @returns {Number} amount of scrolled pixels
253 */
254function getScroll(element) {
255 var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'top';
256
257 var upperSide = side === 'top' ? 'scrollTop' : 'scrollLeft';
258 var nodeName = element.nodeName;
259
260 if (nodeName === 'BODY' || nodeName === 'HTML') {
261 var html = window.document.documentElement;
262 var scrollingElement = window.document.scrollingElement || html;
263 return scrollingElement[upperSide];
264 }
265
266 return element[upperSide];
267}
268
269/*
270 * Sum or subtract the element scroll values (left and top) from a given rect object
271 * @method
272 * @memberof Popper.Utils
273 * @param {Object} rect - Rect object you want to change
274 * @param {HTMLElement} element - The element from the function reads the scroll values
275 * @param {Boolean} subtract - set to true if you want to subtract the scroll values
276 * @return {Object} rect - The modifier rect object
277 */
278function includeScroll(rect, element) {
279 var subtract = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
280
281 var scrollTop = getScroll(element, 'top');
282 var scrollLeft = getScroll(element, 'left');
283 var modifier = subtract ? -1 : 1;
284 rect.top += scrollTop * modifier;
285 rect.bottom += scrollTop * modifier;
286 rect.left += scrollLeft * modifier;
287 rect.right += scrollLeft * modifier;
288 return rect;
289}
290
291/**
292 * Returns the parentNode or the host of the element
293 * @method
294 * @memberof Popper.Utils
295 * @argument {Element} element
296 * @returns {Element} parent
297 */
298function getParentNode(element) {
299 if (element.nodeName === 'HTML') {
300 return element;
301 }
302 return element.parentNode || element.host;
303}
304
305/**
306 * Returns the scrolling parent of the given element
307 * @method
308 * @memberof Popper.Utils
309 * @argument {Element} element
310 * @returns {Element} scroll parent
311 */
312function getScrollParent(element) {
313 // Return body, `getScroll` will take care to get the correct `scrollTop` from it
314 if (!element || ['HTML', 'BODY', '#document'].indexOf(element.nodeName) !== -1) {
315 return window.document.body;
316 }
317
318 // Firefox want us to check `-x` and `-y` variations as well
319
320 var _getStyleComputedProp = getStyleComputedProperty(element),
321 overflow = _getStyleComputedProp.overflow,
322 overflowX = _getStyleComputedProp.overflowX,
323 overflowY = _getStyleComputedProp.overflowY;
324
325 if (/(auto|scroll)/.test(overflow + overflowY + overflowX)) {
326 return element;
327 }
328
329 return getScrollParent(getParentNode(element));
330}
331
332/*
333 * Helper to detect borders of a given element
334 * @method
335 * @memberof Popper.Utils
336 * @param {CSSStyleDeclaration} styles - result of `getStyleComputedProperty` on the given element
337 * @param {String} axis - `x` or `y`
338 * @return {Number} borders - the borders size of the given axis
339 */
340
341function getBordersSize(styles, axis) {
342 var sideA = axis === 'x' ? 'Left' : 'Top';
343 var sideB = sideA === 'Left' ? 'Right' : 'Bottom';
344
345 return +styles['border' + sideA + 'Width'].split('px')[0] + +styles['border' + sideB + 'Width'].split('px')[0];
346}
347
348function getWindowSizes() {
349 var body = window.document.body;
350 var html = window.document.documentElement;
351 return {
352 height: Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight),
353 width: Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth)
354 };
355}
356
357var classCallCheck = function (instance, Constructor) {
358 if (!(instance instanceof Constructor)) {
359 throw new TypeError("Cannot call a class as a function");
360 }
361};
362
363var createClass = function () {
364 function defineProperties(target, props) {
365 for (var i = 0; i < props.length; i++) {
366 var descriptor = props[i];
367 descriptor.enumerable = descriptor.enumerable || false;
368 descriptor.configurable = true;
369 if ("value" in descriptor) descriptor.writable = true;
370 Object.defineProperty(target, descriptor.key, descriptor);
371 }
372 }
373
374 return function (Constructor, protoProps, staticProps) {
375 if (protoProps) defineProperties(Constructor.prototype, protoProps);
376 if (staticProps) defineProperties(Constructor, staticProps);
377 return Constructor;
378 };
379}();
380
381
382
383
384
385var defineProperty = function (obj, key, value) {
386 if (key in obj) {
387 Object.defineProperty(obj, key, {
388 value: value,
389 enumerable: true,
390 configurable: true,
391 writable: true
392 });
393 } else {
394 obj[key] = value;
395 }
396
397 return obj;
398};
399
400var _extends = Object.assign || function (target) {
401 for (var i = 1; i < arguments.length; i++) {
402 var source = arguments[i];
403
404 for (var key in source) {
405 if (Object.prototype.hasOwnProperty.call(source, key)) {
406 target[key] = source[key];
407 }
408 }
409 }
410
411 return target;
412};
413
414/**
415 * Given element offsets, generate an output similar to getBoundingClientRect
416 * @method
417 * @memberof Popper.Utils
418 * @argument {Object} offsets
419 * @returns {Object} ClientRect like output
420 */
421function getClientRect(offsets) {
422 return _extends({}, offsets, {
423 right: offsets.left + offsets.width,
424 bottom: offsets.top + offsets.height
425 });
426}
427
428/**
429 * Tells if you are running Internet Explorer 10
430 * @method
431 * @memberof Popper.Utils
432 * @returns {Boolean} isIE10
433 */
434var isIE10 = undefined;
435
436var isIE10$1 = function () {
437 if (isIE10 === undefined) {
438 isIE10 = navigator.appVersion.indexOf('MSIE 10') !== -1;
439 }
440 return isIE10;
441};
442
443/**
444 * Get bounding client rect of given element
445 * @method
446 * @memberof Popper.Utils
447 * @param {HTMLElement} element
448 * @return {Object} client rect
449 */
450function getBoundingClientRect(element) {
451 var rect = {};
452
453 // IE10 10 FIX: Please, don't ask, the element isn't
454 // considered in DOM in some circumstances...
455 // This isn't reproducible in IE10 compatibility mode of IE11
456 if (isIE10$1()) {
457 try {
458 rect = element.getBoundingClientRect();
459 var scrollTop = getScroll(element, 'top');
460 var scrollLeft = getScroll(element, 'left');
461 rect.top += scrollTop;
462 rect.left += scrollLeft;
463 rect.bottom += scrollTop;
464 rect.right += scrollLeft;
465 } catch (err) {}
466 } else {
467 rect = element.getBoundingClientRect();
468 }
469
470 var result = {
471 left: rect.left,
472 top: rect.top,
473 width: rect.right - rect.left,
474 height: rect.bottom - rect.top
475 };
476
477 // subtract scrollbar size from sizes
478 var sizes = element.nodeName === 'HTML' ? getWindowSizes() : {};
479 var width = sizes.width || element.clientWidth || result.right - result.left;
480 var height = sizes.height || element.clientHeight || result.bottom - result.top;
481
482 var horizScrollbar = element.offsetWidth - width;
483 var vertScrollbar = element.offsetHeight - height;
484
485 // if an hypothetical scrollbar is detected, we must be sure it's not a `border`
486 // we make this check conditional for performance reasons
487 if (horizScrollbar || vertScrollbar) {
488 var styles = getStyleComputedProperty(element);
489 horizScrollbar -= getBordersSize(styles, 'x');
490 vertScrollbar -= getBordersSize(styles, 'y');
491
492 result.width -= horizScrollbar;
493 result.height -= vertScrollbar;
494 }
495
496 return getClientRect(result);
497}
498
499function getOffsetRectRelativeToArbitraryNode(children, parent) {
500 var isIE10 = isIE10$1();
501 var isHTML = parent.nodeName === 'HTML';
502 var childrenRect = getBoundingClientRect(children);
503 var parentRect = getBoundingClientRect(parent);
504 var scrollParent = getScrollParent(children);
505 var offsets = getClientRect({
506 top: childrenRect.top - parentRect.top,
507 left: childrenRect.left - parentRect.left,
508 width: childrenRect.width,
509 height: childrenRect.height
510 });
511
512 // Subtract margins of documentElement in case it's being used as parent
513 // we do this only on HTML because it's the only element that behaves
514 // differently when margins are applied to it. The margins are included in
515 // the box of the documentElement, in the other cases not.
516 if (isHTML || parent.nodeName === 'BODY') {
517 var styles = getStyleComputedProperty(parent);
518 var borderTopWidth = isIE10 && isHTML ? 0 : +styles.borderTopWidth.split('px')[0];
519 var borderLeftWidth = isIE10 && isHTML ? 0 : +styles.borderLeftWidth.split('px')[0];
520 var marginTop = isIE10 && isHTML ? 0 : +styles.marginTop.split('px')[0];
521 var marginLeft = isIE10 && isHTML ? 0 : +styles.marginLeft.split('px')[0];
522
523 offsets.top -= borderTopWidth - marginTop;
524 offsets.bottom -= borderTopWidth - marginTop;
525 offsets.left -= borderLeftWidth - marginLeft;
526 offsets.right -= borderLeftWidth - marginLeft;
527
528 // Attach marginTop and marginLeft because in some circumstances we may need them
529 offsets.marginTop = marginTop;
530 offsets.marginLeft = marginLeft;
531 }
532
533 if (parent.contains(scrollParent) && (isIE10 || scrollParent.nodeName !== 'BODY')) {
534 offsets = includeScroll(offsets, parent);
535 }
536
537 return offsets;
538}
539
540/**
541 * Get offsets to the reference element
542 * @method
543 * @memberof Popper.Utils
544 * @param {Object} state
545 * @param {Element} popper - the popper element
546 * @param {Element} reference - the reference element (the popper will be relative to this)
547 * @returns {Object} An object containing the offsets which will be applied to the popper
548 */
549function getReferenceOffsets(state, popper, reference) {
550 var commonOffsetParent = findCommonOffsetParent(popper, reference);
551 return getOffsetRectRelativeToArbitraryNode(reference, commonOffsetParent);
552}
553
554/**
555 * Get the outer sizes of the given element (offset size + margins)
556 * @method
557 * @memberof Popper.Utils
558 * @argument {Element} element
559 * @returns {Object} object containing width and height properties
560 */
561function getOuterSizes(element) {
562 var styles = window.getComputedStyle(element);
563 var x = parseFloat(styles.marginTop) + parseFloat(styles.marginBottom);
564 var y = parseFloat(styles.marginLeft) + parseFloat(styles.marginRight);
565 var result = {
566 width: element.offsetWidth + y,
567 height: element.offsetHeight + x
568 };
569 return result;
570}
571
572/**
573 * Get the opposite placement of the given one/
574 * @method
575 * @memberof Popper.Utils
576 * @argument {String} placement
577 * @returns {String} flipped placement
578 */
579function getOppositePlacement(placement) {
580 var hash = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' };
581 return placement.replace(/left|right|bottom|top/g, function (matched) {
582 return hash[matched];
583 });
584}
585
586/**
587 * Get offsets to the popper
588 * @method
589 * @memberof Popper.Utils
590 * @param {Object} position - CSS position the Popper will get applied
591 * @param {HTMLElement} popper - the popper element
592 * @param {Object} referenceOffsets - the reference offsets (the popper will be relative to this)
593 * @param {String} placement - one of the valid placement options
594 * @returns {Object} popperOffsets - An object containing the offsets which will be applied to the popper
595 */
596function getPopperOffsets(position, popper, referenceOffsets, placement) {
597 placement = placement.split('-')[0];
598
599 // Get popper node sizes
600 var popperRect = getOuterSizes(popper);
601
602 // Add position, width and height to our offsets object
603 var popperOffsets = {
604 position: position,
605 width: popperRect.width,
606 height: popperRect.height
607 };
608
609 // depending by the popper placement we have to compute its offsets slightly differently
610 var isHoriz = ['right', 'left'].indexOf(placement) !== -1;
611 var mainSide = isHoriz ? 'top' : 'left';
612 var secondarySide = isHoriz ? 'left' : 'top';
613 var measurement = isHoriz ? 'height' : 'width';
614 var secondaryMeasurement = !isHoriz ? 'height' : 'width';
615
616 popperOffsets[mainSide] = referenceOffsets[mainSide] + referenceOffsets[measurement] / 2 - popperRect[measurement] / 2;
617 if (placement === secondarySide) {
618 popperOffsets[secondarySide] = referenceOffsets[secondarySide] - popperRect[secondaryMeasurement];
619 } else {
620 popperOffsets[secondarySide] = referenceOffsets[getOppositePlacement(secondarySide)];
621 }
622
623 return popperOffsets;
624}
625
626/**
627 * Check if the given variable is a function
628 * @method
629 * @memberof Popper.Utils
630 * @argument {*} functionToCheck - variable to check
631 * @returns {Boolean} answer to: is a function?
632 */
633function isFunction(functionToCheck) {
634 var getType = {};
635 return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
636}
637
638function attachToScrollParents(scrollParent, event, callback, scrollParents) {
639 var isBody = scrollParent.nodeName === 'BODY';
640 var target = isBody ? window : scrollParent;
641 target.addEventListener(event, callback, { passive: true });
642
643 if (!isBody) {
644 attachToScrollParents(getScrollParent(target.parentNode), event, callback, scrollParents);
645 }
646 scrollParents.push(target);
647}
648
649/**
650 * Setup needed event listeners used to update the popper position
651 * @method
652 * @memberof Popper.Utils
653 * @private
654 */
655function setupEventListeners(reference, options, state, updateBound) {
656 // Resize event listener on window
657 state.updateBound = updateBound;
658 window.addEventListener('resize', state.updateBound, { passive: true });
659
660 // Scroll event listener on scroll parents
661 var scrollElement = getScrollParent(reference);
662 attachToScrollParents(scrollElement, 'scroll', state.updateBound, state.scrollParents);
663 state.scrollElement = scrollElement;
664 state.eventsEnabled = true;
665
666 return state;
667}
668
669/**
670 * Remove event listeners used to update the popper position
671 * @method
672 * @memberof Popper.Utils
673 * @private
674 */
675function removeEventListeners(reference, state) {
676 // Remove resize event listener on window
677 window.removeEventListener('resize', state.updateBound);
678
679 // Remove scroll event listener on scroll parents
680 state.scrollParents.forEach(function (target) {
681 target.removeEventListener('scroll', state.updateBound);
682 });
683
684 // Reset state
685 state.updateBound = null;
686 state.scrollParents = [];
687 state.scrollElement = null;
688 state.eventsEnabled = false;
689 return state;
690}
691
692/**
693 * Mimics the `find` method of Array
694 * @method
695 * @memberof Popper.Utils
696 * @argument {Array} arr
697 * @argument prop
698 * @argument value
699 * @returns index or -1
700 */
701function find(arr, check) {
702 // use native find if supported
703 if (Array.prototype.find) {
704 return arr.find(check);
705 }
706
707 // use `filter` to obtain the same behavior of `find`
708 return arr.filter(check)[0];
709}
710
711/**
712 * Return the index of the matching object
713 * @method
714 * @memberof Popper.Utils
715 * @argument {Array} arr
716 * @argument prop
717 * @argument value
718 * @returns index or -1
719 */
720function findIndex(arr, prop, value) {
721 // use native findIndex if supported
722 if (Array.prototype.findIndex) {
723 return arr.findIndex(function (cur) {
724 return cur[prop] === value;
725 });
726 }
727
728 // use `find` + `indexOf` if `findIndex` isn't supported
729 var match = find(arr, function (obj) {
730 return obj[prop] === value;
731 });
732 return arr.indexOf(match);
733}
734
735/**
736 * Loop trough the list of modifiers and run them in order, each of them will then edit the data object
737 * @method
738 * @memberof Popper.Utils
739 * @param {Object} data
740 * @param {Array} modifiers
741 * @param {Function} ends
742 */
743function runModifiers(modifiers, data, ends) {
744 var modifiersToRun = ends === undefined ? modifiers : modifiers.slice(0, findIndex(modifiers, 'name', ends));
745
746 modifiersToRun.forEach(function (modifier) {
747 if (modifier.function) {
748 console.warn('`modifier.function` is deprecated, use `modifier.fn`!');
749 }
750 var fn = modifier.function || modifier.fn;
751 if (modifier.enabled && isFunction(fn)) {
752 data = fn(data, modifier);
753 }
754 });
755
756 return data;
757}
758
759/**
760 * Helper used to know if the given modifier is enabled.
761 * @method
762 * @memberof Popper.Utils
763 * @returns {Boolean}
764 */
765function isModifierEnabled(modifiers, modifierName) {
766 return modifiers.some(function (_ref) {
767 var name = _ref.name,
768 enabled = _ref.enabled;
769 return enabled && name === modifierName;
770 });
771}
772
773function getViewportOffsetRectRelativeToArtbitraryNode(element) {
774 var html = window.document.documentElement;
775 var relativeOffset = getOffsetRectRelativeToArbitraryNode(element, html);
776 var width = Math.max(html.clientWidth, window.innerWidth || 0);
777 var height = Math.max(html.clientHeight, window.innerHeight || 0);
778
779 var scrollTop = getScroll(html);
780 var scrollLeft = getScroll(html, 'left');
781
782 var offset = {
783 top: scrollTop - relativeOffset.top + relativeOffset.marginTop,
784 left: scrollLeft - relativeOffset.left + relativeOffset.marginLeft,
785 width: width,
786 height: height
787 };
788
789 return getClientRect(offset);
790}
791
792/**
793 * Check if the given element is fixed or is inside a fixed parent
794 * @method
795 * @memberof Popper.Utils
796 * @argument {Element} element
797 * @argument {Element} customContainer
798 * @returns {Boolean} answer to "isFixed?"
799 */
800function isFixed(element) {
801 var nodeName = element.nodeName;
802 if (nodeName === 'BODY' || nodeName === 'HTML') {
803 return false;
804 }
805 if (getStyleComputedProperty(element, 'position') === 'fixed') {
806 return true;
807 }
808 return isFixed(getParentNode(element));
809}
810
811/**
812 * Computed the boundaries limits and return them
813 * @method
814 * @memberof Popper.Utils
815 * @param {Object} data - Object containing the property "offsets" generated by `_getOffsets`
816 * @param {Number} padding - Boundaries padding
817 * @param {Element} boundariesElement - Element used to define the boundaries
818 * @returns {Object} Coordinates of the boundaries
819 */
820function getBoundaries(popper, reference, padding, boundariesElement) {
821 // NOTE: 1 DOM access here
822 var boundaries = { top: 0, left: 0 };
823 var offsetParent = findCommonOffsetParent(popper, reference);
824
825 // Handle viewport case
826 if (boundariesElement === 'viewport') {
827 boundaries = getViewportOffsetRectRelativeToArtbitraryNode(offsetParent);
828 } else {
829 // Handle other cases based on DOM element used as boundaries
830 var boundariesNode = void 0;
831 if (boundariesElement === 'scrollParent') {
832 boundariesNode = getScrollParent(getParentNode(popper));
833 if (boundariesNode.nodeName === 'BODY') {
834 boundariesNode = window.document.documentElement;
835 }
836 } else if (boundariesElement === 'window') {
837 boundariesNode = window.document.documentElement;
838 } else {
839 boundariesNode = boundariesElement;
840 }
841
842 var offsets = getOffsetRectRelativeToArbitraryNode(boundariesNode, offsetParent);
843
844 // In case of HTML, we need a different computation
845 if (boundariesNode.nodeName === 'HTML' && !isFixed(offsetParent)) {
846 var _getWindowSizes = getWindowSizes(),
847 height = _getWindowSizes.height,
848 width = _getWindowSizes.width;
849
850 boundaries.top += offsets.top - offsets.marginTop;
851 boundaries.bottom = height + offsets.top;
852 boundaries.left += offsets.left - offsets.marginLeft;
853 boundaries.right = width + offsets.left;
854 } else {
855 // for all the other DOM elements, this one is good
856 boundaries = offsets;
857 }
858 }
859
860 // Add paddings
861 boundaries.left += padding;
862 boundaries.top += padding;
863 boundaries.right -= padding;
864 boundaries.bottom -= padding;
865
866 return boundaries;
867}
868
869/**
870 * Utility used to transform the `auto` placement to the placement with more
871 * available space.
872 * @method
873 * @memberof Popper.Utils
874 * @argument {Object} data - The data object generated by update method
875 * @argument {Object} options - Modifiers configuration and options
876 * @returns {Object} The data object, properly modified
877 */
878function computeAutoPlacement(placement, refRect, popper, reference, boundariesElement) {
879 if (placement.indexOf('auto') === -1) {
880 return placement;
881 }
882
883 var boundaries = getBoundaries(popper, reference, 0, boundariesElement);
884
885 var sides = {
886 top: refRect.top - boundaries.top,
887 right: boundaries.right - refRect.right,
888 bottom: boundaries.bottom - refRect.bottom,
889 left: refRect.left - boundaries.left
890 };
891
892 var computedPlacement = Object.keys(sides).sort(function (a, b) {
893 return sides[b] - sides[a];
894 })[0];
895 var variation = placement.split('-')[1];
896
897 return computedPlacement + (variation ? '-' + variation : '');
898}
899
900var 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'];
901
902/**
903 * Set the attributes to the given popper
904 * @method
905 * @memberof Popper.Utils
906 * @argument {Element} element - Element to apply the attributes to
907 * @argument {Object} styles - Object with a list of properties and values which will be applied to the element
908 */
909function setAttributes(element, attributes) {
910 Object.keys(attributes).forEach(function (prop) {
911 var value = attributes[prop];
912 if (value !== false) {
913 element.setAttribute(prop, attributes[prop]);
914 } else {
915 element.removeAttribute(prop);
916 }
917 });
918}
919
920/**
921 * @function
922 * @memberof Modifiers
923 * @argument {Object} data - The data object generated by `update` method
924 * @argument {Object} data.styles - List of style properties - values to apply to popper element
925 * @argument {Object} data.attributes - List of attribute properties - values to apply to popper element
926 * @argument {Object} options - Modifiers configuration and options
927 * @returns {Object} The same data object
928 */
929function applyStyle(data, options) {
930 // apply the final offsets to the popper
931 // NOTE: 1 DOM access here
932 var styles = {
933 position: data.offsets.popper.position
934 };
935
936 var attributes = {
937 'x-placement': data.placement
938 };
939
940 // round top and left to avoid blurry text
941 var left = Math.round(data.offsets.popper.left);
942 var top = Math.round(data.offsets.popper.top);
943
944 // if gpuAcceleration is set to true and transform is supported,
945 // we use `translate3d` to apply the position to the popper we
946 // automatically use the supported prefixed version if needed
947 var prefixedProperty = getSupportedPropertyName('transform');
948 if (options.gpuAcceleration && prefixedProperty) {
949 styles[prefixedProperty] = 'translate3d(' + left + 'px, ' + top + 'px, 0)';
950 styles.top = 0;
951 styles.left = 0;
952 styles.willChange = 'transform';
953 } else {
954 // othwerise, we use the standard `left` and `top` properties
955 styles.left = left;
956 styles.top = top;
957 styles.willChange = 'top, left';
958 }
959
960 // any property present in `data.styles` will be applied to the popper,
961 // in this way we can make the 3rd party modifiers add custom styles to it
962 // Be aware, modifiers could override the properties defined in the previous
963 // lines of this modifier!
964 setStyles(data.instance.popper, _extends({}, styles, data.styles));
965
966 // any property present in `data.attributes` will be applied to the popper,
967 // they will be set as HTML attributes of the element
968 setAttributes(data.instance.popper, _extends({}, attributes, data.attributes));
969
970 // if the arrow style has been computed, apply the arrow style
971 if (data.offsets.arrow) {
972 setStyles(data.arrowElement, data.offsets.arrow);
973 }
974
975 return data;
976}
977
978/**
979 * Set the x-placement attribute before everything else because it could be used to add margins to the popper
980 * margins needs to be calculated to get the correct popper offsets
981 * @method
982 * @memberof Popper.modifiers
983 * @param {HTMLElement} reference - The reference element used to position the popper
984 * @param {HTMLElement} popper - The HTML element used as popper.
985 * @param {Object} options - Popper.js options
986 */
987function applyStyleOnLoad(reference, popper, options, modifierOptions, state) {
988 // compute reference element offsets
989 var referenceOffsets = getReferenceOffsets(state, popper, reference);
990
991 // compute auto placement, store placement inside the data object,
992 // modifiers will be able to edit `placement` if needed
993 // and refer to originalPlacement to know the original value
994 var placement = computeAutoPlacement(options.placement, referenceOffsets, popper, reference, options.modifiers.flip.boundariesElement);
995
996 popper.setAttribute('x-placement', placement);
997 return options;
998}
999
1000/**
1001 * Helper used to know if the given modifier depends from another one.
1002 * It checks if the needed modifier is listed and enabled.
1003 * @method
1004 * @memberof Popper.Utils
1005 * @param {Array} modifiers - list of modifiers
1006 * @param {String} requestingName - name of requesting modifier
1007 * @param {String} requestedName - name of requested modifier
1008 * @returns {Boolean}
1009 */
1010function isModifierRequired(modifiers, requestingName, requestedName) {
1011 var requesting = find(modifiers, function (_ref) {
1012 var name = _ref.name;
1013 return name === requestingName;
1014 });
1015
1016 var isRequired = !!requesting && modifiers.some(function (modifier) {
1017 return modifier.name === requestedName && modifier.enabled && modifier.order < requesting.order;
1018 });
1019
1020 if (!isRequired) {
1021 var _requesting = '`' + requestingName + '`';
1022 var requested = '`' + requestedName + '`';
1023 console.warn(requested + ' modifier is required by ' + _requesting + ' modifier in order to work, be sure to include it before ' + _requesting + '!');
1024 }
1025 return isRequired;
1026}
1027
1028/**
1029 * @function
1030 * @memberof Modifiers
1031 * @argument {Object} data - The data object generated by update method
1032 * @argument {Object} options - Modifiers configuration and options
1033 * @returns {Object} The data object, properly modified
1034 */
1035function arrow(data, options) {
1036 // arrow depends on keepTogether in order to work
1037 if (!isModifierRequired(data.instance.modifiers, 'arrow', 'keepTogether')) {
1038 return data;
1039 }
1040
1041 var arrowElement = options.element;
1042
1043 // if arrowElement is a string, suppose it's a CSS selector
1044 if (typeof arrowElement === 'string') {
1045 arrowElement = data.instance.popper.querySelector(arrowElement);
1046
1047 // if arrowElement is not found, don't run the modifier
1048 if (!arrowElement) {
1049 return data;
1050 }
1051 } else {
1052 // if the arrowElement isn't a query selector we must check that the
1053 // provided DOM node is child of its popper node
1054 if (!data.instance.popper.contains(arrowElement)) {
1055 console.warn('WARNING: `arrow.element` must be child of its popper element!');
1056 return data;
1057 }
1058 }
1059
1060 var placement = data.placement.split('-')[0];
1061 var popper = getClientRect(data.offsets.popper);
1062 var reference = data.offsets.reference;
1063 var isVertical = ['left', 'right'].indexOf(placement) !== -1;
1064
1065 var len = isVertical ? 'height' : 'width';
1066 var side = isVertical ? 'top' : 'left';
1067 var altSide = isVertical ? 'left' : 'top';
1068 var opSide = isVertical ? 'bottom' : 'right';
1069 var arrowElementSize = getOuterSizes(arrowElement)[len];
1070
1071 //
1072 // extends keepTogether behavior making sure the popper and its reference have enough pixels in conjuction
1073 //
1074
1075 // top/left side
1076 if (reference[opSide] - arrowElementSize < popper[side]) {
1077 data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowElementSize);
1078 }
1079 // bottom/right side
1080 if (reference[side] + arrowElementSize > popper[opSide]) {
1081 data.offsets.popper[side] += reference[side] + arrowElementSize - popper[opSide];
1082 }
1083
1084 // compute center of the popper
1085 var center = reference[side] + reference[len] / 2 - arrowElementSize / 2;
1086
1087 // Compute the sideValue using the updated popper offsets
1088 var sideValue = center - getClientRect(data.offsets.popper)[side];
1089
1090 // prevent arrowElement from being placed not contiguously to its popper
1091 sideValue = Math.max(Math.min(popper[len] - arrowElementSize, sideValue), 0);
1092
1093 data.arrowElement = arrowElement;
1094 data.offsets.arrow = {};
1095 data.offsets.arrow[side] = sideValue;
1096 data.offsets.arrow[altSide] = ''; // make sure to unset any eventual altSide value from the DOM node
1097
1098 return data;
1099}
1100
1101/**
1102 * Get the opposite placement variation of the given one/
1103 * @method
1104 * @memberof Popper.Utils
1105 * @argument {String} placement variation
1106 * @returns {String} flipped placement variation
1107 */
1108function getOppositeVariation(variation) {
1109 if (variation === 'end') {
1110 return 'start';
1111 } else if (variation === 'start') {
1112 return 'end';
1113 }
1114 return variation;
1115}
1116
1117// Get rid of `auto` `auto-start` and `auto-end`
1118var validPlacements = placements.slice(3);
1119
1120/**
1121 * Given an initial placement, returns all the subsequent placements
1122 * clockwise (or counter-clockwise).
1123 *
1124 * @method
1125 * @memberof Popper.Utils
1126 * @argument {String} placement - A valid placement (it accepts variations)
1127 * @argument {Boolean} counter - Set to true to walk the placements counterclockwise
1128 * @returns {Array} placements including their variations
1129 */
1130function clockwise(placement) {
1131 var counter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
1132
1133 var index = validPlacements.indexOf(placement);
1134 var arr = validPlacements.slice(index + 1).concat(validPlacements.slice(0, index));
1135 return counter ? arr.reverse() : arr;
1136}
1137
1138var BEHAVIORS = {
1139 FLIP: 'flip',
1140 CLOCKWISE: 'clockwise',
1141 COUNTERCLOCKWISE: 'counterclockwise'
1142};
1143
1144/**
1145 * @function
1146 * @memberof Modifiers
1147 * @argument {Object} data - The data object generated by update method
1148 * @argument {Object} options - Modifiers configuration and options
1149 * @returns {Object} The data object, properly modified
1150 */
1151function flip(data, options) {
1152 // if `inner` modifier is enabled, we can't use the `flip` modifier
1153 if (isModifierEnabled(data.instance.modifiers, 'inner')) {
1154 return data;
1155 }
1156
1157 if (data.flipped && data.placement === data.originalPlacement) {
1158 // seems like flip is trying to loop, probably there's not enough space on any of the flippable sides
1159 return data;
1160 }
1161
1162 var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, options.boundariesElement);
1163
1164 var placement = data.placement.split('-')[0];
1165 var placementOpposite = getOppositePlacement(placement);
1166 var variation = data.placement.split('-')[1] || '';
1167
1168 var flipOrder = [];
1169
1170 switch (options.behavior) {
1171 case BEHAVIORS.FLIP:
1172 flipOrder = [placement, placementOpposite];
1173 break;
1174 case BEHAVIORS.CLOCKWISE:
1175 flipOrder = clockwise(placement);
1176 break;
1177 case BEHAVIORS.COUNTERCLOCKWISE:
1178 flipOrder = clockwise(placement, true);
1179 break;
1180 default:
1181 flipOrder = options.behavior;
1182 }
1183
1184 flipOrder.forEach(function (step, index) {
1185 if (placement !== step || flipOrder.length === index + 1) {
1186 return data;
1187 }
1188
1189 placement = data.placement.split('-')[0];
1190 placementOpposite = getOppositePlacement(placement);
1191
1192 var popperOffsets = getClientRect(data.offsets.popper);
1193 var refOffsets = data.offsets.reference;
1194
1195 // using floor because the reference offsets may contain decimals we are not going to consider here
1196 var floor = Math.floor;
1197 var overlapsRef = placement === 'left' && floor(popperOffsets.right) > floor(refOffsets.left) || placement === 'right' && floor(popperOffsets.left) < floor(refOffsets.right) || placement === 'top' && floor(popperOffsets.bottom) > floor(refOffsets.top) || placement === 'bottom' && floor(popperOffsets.top) < floor(refOffsets.bottom);
1198
1199 var overflowsLeft = floor(popperOffsets.left) < floor(boundaries.left);
1200 var overflowsRight = floor(popperOffsets.right) > floor(boundaries.right);
1201 var overflowsTop = floor(popperOffsets.top) < floor(boundaries.top);
1202 var overflowsBottom = floor(popperOffsets.bottom) > floor(boundaries.bottom);
1203
1204 var overflowsBoundaries = placement === 'left' && overflowsLeft || placement === 'right' && overflowsRight || placement === 'top' && overflowsTop || placement === 'bottom' && overflowsBottom;
1205
1206 // flip the variation if required
1207 var isVertical = ['top', 'bottom'].indexOf(placement) !== -1;
1208 var flippedVariation = !!options.flipVariations && (isVertical && variation === 'start' && overflowsLeft || isVertical && variation === 'end' && overflowsRight || !isVertical && variation === 'start' && overflowsTop || !isVertical && variation === 'end' && overflowsBottom);
1209
1210 if (overlapsRef || overflowsBoundaries || flippedVariation) {
1211 // this boolean to detect any flip loop
1212 data.flipped = true;
1213
1214 if (overlapsRef || overflowsBoundaries) {
1215 placement = flipOrder[index + 1];
1216 }
1217
1218 if (flippedVariation) {
1219 variation = getOppositeVariation(variation);
1220 }
1221
1222 data.placement = placement + (variation ? '-' + variation : '');
1223 data.offsets.popper = getPopperOffsets(data.instance.state.position, data.instance.popper, data.offsets.reference, data.placement);
1224
1225 data = runModifiers(data.instance.modifiers, data, 'flip');
1226 }
1227 });
1228 return data;
1229}
1230
1231/**
1232 * @function
1233 * @memberof Modifiers
1234 * @argument {Object} data - The data object generated by update method
1235 * @argument {Object} options - Modifiers configuration and options
1236 * @returns {Object} The data object, properly modified
1237 */
1238function keepTogether(data) {
1239 var popper = getClientRect(data.offsets.popper);
1240 var reference = data.offsets.reference;
1241 var placement = data.placement.split('-')[0];
1242 var floor = Math.floor;
1243 var isVertical = ['top', 'bottom'].indexOf(placement) !== -1;
1244 var side = isVertical ? 'right' : 'bottom';
1245 var opSide = isVertical ? 'left' : 'top';
1246 var measurement = isVertical ? 'width' : 'height';
1247
1248 if (popper[side] < floor(reference[opSide])) {
1249 data.offsets.popper[opSide] = floor(reference[opSide]) - popper[measurement];
1250 }
1251 if (popper[opSide] > floor(reference[side])) {
1252 data.offsets.popper[opSide] = floor(reference[side]);
1253 }
1254
1255 return data;
1256}
1257
1258/**
1259 * @function
1260 * @memberof Modifiers
1261 * @argument {Object} data - The data object generated by update method
1262 * @argument {Object} options - Modifiers configuration and options
1263 * @argument {Number|String} options.offset=0
1264 * The offset value as described in the modifier description
1265 * @returns {Object} The data object, properly modified
1266 */
1267function offset(data, options) {
1268 var placement = data.placement;
1269 var popper = data.offsets.popper;
1270
1271 var offsets = void 0;
1272 if (isNumeric(options.offset)) {
1273 offsets = [options.offset, 0];
1274 } else {
1275 // split the offset in case we are providing a pair of offsets separated
1276 // by a blank space
1277 offsets = options.offset.split(' ');
1278
1279 // itherate through each offset to compute them in case they are percentages
1280 offsets = offsets.map(function (offset, index) {
1281 // separate value from unit
1282 var split = offset.match(/(\-?\d*\.?\d*)(.*)/);
1283 var value = +split[1];
1284 var unit = split[2];
1285
1286 // use height if placement is left or right and index is 0 otherwise use width
1287 // in this way the first offset will use an axis and the second one
1288 // will use the other one
1289 var useHeight = placement.indexOf('right') !== -1 || placement.indexOf('left') !== -1;
1290
1291 if (index === 1) {
1292 useHeight = !useHeight;
1293 }
1294
1295 var measurement = useHeight ? 'height' : 'width';
1296
1297 // if is a percentage relative to the popper (%p), we calculate the value of it using
1298 // as base the sizes of the popper
1299 // if is a percentage (% or %r), we calculate the value of it using as base the
1300 // sizes of the reference element
1301 if (unit.indexOf('%') === 0) {
1302 var element = void 0;
1303 switch (unit) {
1304 case '%p':
1305 element = data.offsets.popper;
1306 break;
1307 case '%':
1308 case '$r':
1309 default:
1310 element = data.offsets.reference;
1311 }
1312
1313 var rect = getClientRect(element);
1314 var len = rect[measurement];
1315 return len / 100 * value;
1316 } else if (unit === 'vh' || unit === 'vw') {
1317 // if is a vh or vw, we calculate the size based on the viewport
1318 var size = void 0;
1319 if (unit === 'vh') {
1320 size = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
1321 } else {
1322 size = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
1323 }
1324 return size / 100 * value;
1325 } else if (unit === 'px') {
1326 // if is an explicit pixel unit, we get rid of the unit and keep the value
1327 return +value;
1328 } else {
1329 // if is an implicit unit, it's px, and we return just the value
1330 return +offset;
1331 }
1332 });
1333 }
1334
1335 if (data.placement.indexOf('left') !== -1) {
1336 popper.top += offsets[0];
1337 popper.left -= offsets[1] || 0;
1338 } else if (data.placement.indexOf('right') !== -1) {
1339 popper.top += offsets[0];
1340 popper.left += offsets[1] || 0;
1341 } else if (data.placement.indexOf('top') !== -1) {
1342 popper.left += offsets[0];
1343 popper.top -= offsets[1] || 0;
1344 } else if (data.placement.indexOf('bottom') !== -1) {
1345 popper.left += offsets[0];
1346 popper.top += offsets[1] || 0;
1347 }
1348 return data;
1349}
1350
1351/**
1352 * @function
1353 * @memberof Modifiers
1354 * @argument {Object} data - The data object generated by `update` method
1355 * @argument {Object} options - Modifiers configuration and options
1356 * @returns {Object} The data object, properly modified
1357 */
1358function preventOverflow(data, options) {
1359 var boundariesElement = options.boundariesElement || getOffsetParent(data.instance.popper);
1360 var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, boundariesElement);
1361 options.boundaries = boundaries;
1362
1363 var order = options.priority;
1364 var popper = getClientRect(data.offsets.popper);
1365
1366 var check = {
1367 primary: function primary(placement) {
1368 var value = popper[placement];
1369 if (popper[placement] < boundaries[placement] && !options.escapeWithReference) {
1370 value = Math.max(popper[placement], boundaries[placement]);
1371 }
1372 return defineProperty({}, placement, value);
1373 },
1374 secondary: function secondary(placement) {
1375 var mainSide = placement === 'right' ? 'left' : 'top';
1376 var value = popper[mainSide];
1377 if (popper[placement] > boundaries[placement] && !options.escapeWithReference) {
1378 value = Math.min(popper[mainSide], boundaries[placement] - (placement === 'right' ? popper.width : popper.height));
1379 }
1380 return defineProperty({}, mainSide, value);
1381 }
1382 };
1383
1384 order.forEach(function (placement) {
1385 var side = ['left', 'top'].indexOf(placement) !== -1 ? 'primary' : 'secondary';
1386 popper = _extends({}, popper, check[side](placement));
1387 });
1388
1389 data.offsets.popper = popper;
1390
1391 return data;
1392}
1393
1394/**
1395 * @function
1396 * @memberof Modifiers
1397 * @argument {Object} data - The data object generated by `update` method
1398 * @argument {Object} options - Modifiers configuration and options
1399 * @returns {Object} The data object, properly modified
1400 */
1401function shift(data) {
1402 var placement = data.placement;
1403 var basePlacement = placement.split('-')[0];
1404 var shiftvariation = placement.split('-')[1];
1405
1406 // if shift shiftvariation is specified, run the modifier
1407 if (shiftvariation) {
1408 var reference = data.offsets.reference;
1409 var popper = getClientRect(data.offsets.popper);
1410 var isVertical = ['bottom', 'top'].indexOf(basePlacement) !== -1;
1411 var side = isVertical ? 'left' : 'top';
1412 var measurement = isVertical ? 'width' : 'height';
1413
1414 var shiftOffsets = {
1415 start: defineProperty({}, side, reference[side]),
1416 end: defineProperty({}, side, reference[side] + reference[measurement] - popper[measurement])
1417 };
1418
1419 data.offsets.popper = _extends({}, popper, shiftOffsets[shiftvariation]);
1420 }
1421
1422 return data;
1423}
1424
1425/**
1426 * @function
1427 * @memberof Modifiers
1428 * @argument {Object} data - The data object generated by update method
1429 * @argument {Object} options - Modifiers configuration and options
1430 * @returns {Object} The data object, properly modified
1431 */
1432function hide(data) {
1433 if (!isModifierRequired(data.instance.modifiers, 'hide', 'preventOverflow')) {
1434 return data;
1435 }
1436
1437 var refRect = data.offsets.reference;
1438 var bound = find(data.instance.modifiers, function (modifier) {
1439 return modifier.name === 'preventOverflow';
1440 }).boundaries;
1441
1442 if (refRect.bottom < bound.top || refRect.left > bound.right || refRect.top > bound.bottom || refRect.right < bound.left) {
1443 // Avoid unnecessary DOM access if visibility hasn't changed
1444 if (data.hide === true) {
1445 return data;
1446 }
1447
1448 data.hide = true;
1449 data.attributes['x-out-of-boundaries'] = '';
1450 } else {
1451 // Avoid unnecessary DOM access if visibility hasn't changed
1452 if (data.hide === false) {
1453 return data;
1454 }
1455
1456 data.hide = false;
1457 data.attributes['x-out-of-boundaries'] = false;
1458 }
1459
1460 return data;
1461}
1462
1463/**
1464 * @function
1465 * @memberof Modifiers
1466 * @argument {Object} data - The data object generated by `update` method
1467 * @argument {Object} options - Modifiers configuration and options
1468 * @returns {Object} The data object, properly modified
1469 */
1470function inner(data) {
1471 var placement = data.placement;
1472 var basePlacement = placement.split('-')[0];
1473 var popper = getClientRect(data.offsets.popper);
1474 var reference = getClientRect(data.offsets.reference);
1475 var isHoriz = ['left', 'right'].indexOf(basePlacement) !== -1;
1476
1477 var subtractLength = ['top', 'left'].indexOf(basePlacement) === -1;
1478
1479 popper[isHoriz ? 'left' : 'top'] = reference[placement] - (subtractLength ? popper[isHoriz ? 'width' : 'height'] : 0);
1480
1481 data.placement = getOppositePlacement(placement);
1482 data.offsets.popper = getClientRect(popper);
1483
1484 return data;
1485}
1486
1487/**
1488 * Modifiers are plugins used to alter the behavior of your poppers.
1489 * Popper.js uses a set of 9 modifiers to provide all the basic functionalities
1490 * needed by the library.
1491 *
1492 * Usually you don't want to override the `order`, `fn` and `onLoad` props.
1493 * All the other properties are configurations that could be tweaked.
1494 * @namespace modifiers
1495 */
1496var modifiers = {
1497 /**
1498 * Modifier used to shift the popper on the start or end of its reference element.
1499 * It will read the variation of the `placement` property.
1500 * It can be one either `-end` or `-start`.
1501 * @memberof modifiers
1502 * @inner
1503 */
1504 shift: {
1505 /** @prop {Number} order=100 - Index used to define the order of execution */
1506 order: 100,
1507 /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
1508 enabled: true,
1509 /** @prop {Function} */
1510 fn: shift
1511 },
1512
1513 /**
1514 * The `offset` modifier can shift your popper on both its axis.
1515 *
1516 * It accepts the following units:
1517 * - unitless, interpreted as pixels
1518 * - `%` or `%r`, percentage relative to the length of the reference element
1519 * - `%p`, percentage relative to the length of the popper element
1520 * - `vw`, CSS viewport width unit
1521 * - `vh`, CSS viewport height unit
1522 *
1523 * For length is intended the main axis relative to the placement of the popper.
1524 * This means that if the placement is `top` or `bottom`, the length will be the
1525 * `width`. In case of `left` or `right`, it will be the height.
1526 *
1527 * You can provide a single value (as `Number` or `String`), or a pair of values
1528 * as `String` divided by one white space.
1529 *
1530 * Valid examples are:
1531 * - offset: 10
1532 * - offset: '10%'
1533 * - offset: '10 10'
1534 * - offset: '10% 10'
1535 * @memberof modifiers
1536 * @inner
1537 */
1538 offset: {
1539 /** @prop {Number} order=200 - Index used to define the order of execution */
1540 order: 200,
1541 /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
1542 enabled: true,
1543 /** @prop {Function} */
1544 fn: offset,
1545 /** @prop {Number|String} offset=0
1546 * The offset value as described in the modifier description
1547 */
1548 offset: 0
1549 },
1550
1551 /**
1552 * Modifier used to prevent the popper from being positioned outside the boundary.
1553 *
1554 * An scenario exists where the reference itself is not within the boundaries.
1555 * We can say it has "escaped the boundaries" — or just "escaped".
1556 * In this case we need to decide whether the popper should either:
1557 *
1558 * - detach from the reference and remain "trapped" in the boundaries, or
1559 * - if it should ignore the boundary and "escape with its reference"
1560 *
1561 * When `escapeWithReference` is set to`true` and reference is completely
1562 * outside its boundaries, the popper will overflow (or completely leave)
1563 * the boundaries in order to remain attached to the edge of the reference.
1564 *
1565 * @memberof modifiers
1566 * @inner
1567 */
1568 preventOverflow: {
1569 /** @prop {Number} order=300 - Index used to define the order of execution */
1570 order: 300,
1571 /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
1572 enabled: true,
1573 /** @prop {Function} */
1574 fn: preventOverflow,
1575 /**
1576 * @prop {Array} priority=['left', 'right', 'top', 'bottom']
1577 * Popper will try to prevent overflow following these priorities by default,
1578 * then, it could overflow on the left and on top of the `boundariesElement`
1579 */
1580 priority: ['left', 'right', 'top', 'bottom'],
1581 /**
1582 * @prop {Number} padding=5
1583 * Amount of pixel used to define a minimum distance between the boundaries
1584 * and the popper this makes sure the popper has always a little padding
1585 * between the edges of its container
1586 */
1587 padding: 5,
1588 /**
1589 * @prop {String|HTMLElement} boundariesElement='scrollParent'
1590 * Boundaries used by the modifier, can be `scrollParent`, `window`,
1591 * `viewport` or any DOM element.
1592 */
1593 boundariesElement: 'scrollParent'
1594 },
1595
1596 /**
1597 * Modifier used to make sure the reference and its popper stay near eachothers
1598 * without leaving any gap between the two. Expecially useful when the arrow is
1599 * enabled and you want to assure it to point to its reference element.
1600 * It cares only about the first axis, you can still have poppers with margin
1601 * between the popper and its reference element.
1602 * @memberof modifiers
1603 * @inner
1604 */
1605 keepTogether: {
1606 /** @prop {Number} order=400 - Index used to define the order of execution */
1607 order: 400,
1608 /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
1609 enabled: true,
1610 /** @prop {Function} */
1611 fn: keepTogether
1612 },
1613
1614 /**
1615 * This modifier is used to move the `arrowElement` of the popper to make
1616 * sure it is positioned between the reference element and its popper element.
1617 * It will read the outer size of the `arrowElement` node to detect how many
1618 * pixels of conjuction are needed.
1619 *
1620 * It has no effect if no `arrowElement` is provided.
1621 * @memberof modifiers
1622 * @inner
1623 */
1624 arrow: {
1625 /** @prop {Number} order=500 - Index used to define the order of execution */
1626 order: 500,
1627 /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
1628 enabled: true,
1629 /** @prop {Function} */
1630 fn: arrow,
1631 /** @prop {String|HTMLElement} element='[x-arrow]' - Selector or node used as arrow */
1632 element: '[x-arrow]'
1633 },
1634
1635 /**
1636 * Modifier used to flip the popper's placement when it starts to overlap its
1637 * reference element.
1638 *
1639 * Requires the `preventOverflow` modifier before it in order to work.
1640 *
1641 * **NOTE:** this modifier will interrupt the current update cycle and will
1642 * restart it if it detects the need to flip the placement.
1643 * @memberof modifiers
1644 * @inner
1645 */
1646 flip: {
1647 /** @prop {Number} order=600 - Index used to define the order of execution */
1648 order: 600,
1649 /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
1650 enabled: true,
1651 /** @prop {Function} */
1652 fn: flip,
1653 /**
1654 * @prop {String|Array} behavior='flip'
1655 * The behavior used to change the popper's placement. It can be one of
1656 * `flip`, `clockwise`, `counterclockwise` or an array with a list of valid
1657 * placements (with optional variations).
1658 */
1659 behavior: 'flip',
1660 /**
1661 * @prop {Number} padding=5
1662 * The popper will flip if it hits the edges of the `boundariesElement`
1663 */
1664 padding: 5,
1665 /**
1666 * @prop {String|HTMLElement} boundariesElement='viewport'
1667 * The element which will define the boundaries of the popper position,
1668 * the popper will never be placed outside of the defined boundaries
1669 * (except if keepTogether is enabled)
1670 */
1671 boundariesElement: 'viewport'
1672 },
1673
1674 /**
1675 * Modifier used to make the popper flow toward the inner of the reference element.
1676 * By default, when this modifier is disabled, the popper will be placed outside
1677 * the reference element.
1678 * @memberof modifiers
1679 * @inner
1680 */
1681 inner: {
1682 /** @prop {Number} order=700 - Index used to define the order of execution */
1683 order: 700,
1684 /** @prop {Boolean} enabled=false - Whether the modifier is enabled or not */
1685 enabled: false,
1686 /** @prop {Function} */
1687 fn: inner
1688 },
1689
1690 /**
1691 * Modifier used to hide the popper when its reference element is outside of the
1692 * popper boundaries. It will set a `x-out-of-boundaries` attribute which can
1693 * be used to hide with a CSS selector the popper when its reference is
1694 * out of boundaries.
1695 *
1696 * Requires the `preventOverflow` modifier before it in order to work.
1697 * @memberof modifiers
1698 * @inner
1699 */
1700 hide: {
1701 /** @prop {Number} order=800 - Index used to define the order of execution */
1702 order: 800,
1703 /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
1704 enabled: true,
1705 /** @prop {Function} */
1706 fn: hide
1707 },
1708
1709 /**
1710 * Applies the computed styles to the popper element.
1711 *
1712 * All the DOM manipulations are limited to this modifier. This is useful in case
1713 * you want to integrate Popper.js inside a framework or view library and you
1714 * want to delegate all the DOM manipulations to it.
1715 *
1716 * Just disable this modifier and define you own to achieve the desired effect.
1717 *
1718 * @memberof modifiers
1719 * @inner
1720 */
1721 applyStyle: {
1722 /** @prop {Number} order=900 - Index used to define the order of execution */
1723 order: 900,
1724 /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
1725 enabled: true,
1726 /** @prop {Function} */
1727 fn: applyStyle,
1728 /** @prop {Function} */
1729 onLoad: applyStyleOnLoad,
1730 /**
1731 * @prop {Boolean} gpuAcceleration=true
1732 * If true, it uses the CSS 3d transformation to position the popper.
1733 * Otherwise, it will use the `top` and `left` properties.
1734 */
1735 gpuAcceleration: true
1736 }
1737};
1738
1739/**
1740 * Modifiers can edit the `data` object to change the beheavior of the popper.
1741 * This object contains all the informations used by Popper.js to compute the
1742 * popper position.
1743 * The modifier can edit the data as needed, and then `return` it as result.
1744 *
1745 * @callback Modifiers~modifier
1746 * @param {dataObject} data
1747 * @return {dataObject} modified data
1748 */
1749
1750/**
1751 * The `dataObject` is an object containing all the informations used by Popper.js
1752 * this object get passed to modifiers and to the `onCreate` and `onUpdate` callbacks.
1753 * @name dataObject
1754 * @property {Object} data.instance The Popper.js instance
1755 * @property {String} data.placement Placement applied to popper
1756 * @property {String} data.originalPlacement Placement originally defined on init
1757 * @property {Boolean} data.flipped True if popper has been flipped by flip modifier
1758 * @property {Boolean} data.hide True if the reference element is out of boundaries, useful to know when to hide the popper.
1759 * @property {HTMLElement} data.arrowElement Node used as arrow by arrow modifier
1760 * @property {Object} data.styles Any CSS property defined here will be applied to the popper, it expects the JavaScript nomenclature (eg. `marginBottom`)
1761 * @property {Object} data.boundaries Offsets of the popper boundaries
1762 * @property {Object} data.offsets The measurements of popper, reference and arrow elements.
1763 * @property {Object} data.offsets.popper `top`, `left`, `width`, `height` values
1764 * @property {Object} data.offsets.reference `top`, `left`, `width`, `height` values
1765 * @property {Object} data.offsets.arro] `top` and `left` offsets, only one of them will be different from 0
1766 */
1767
1768// Utils
1769// Modifiers
1770/**
1771 *
1772 * @callback onCreateCallback
1773 * @param {dataObject} data
1774 */
1775
1776/**
1777 *
1778 * @callback onUpdateCallback
1779 * @param {dataObject} data
1780 */
1781
1782/**
1783 * Default options provided to Popper.js constructor.
1784 * These can be overriden using the `options` argument of Popper.js.
1785 * To override an option, simply pass as 3rd argument an object with the same
1786 * structure of {defaults}, example:
1787 * ```
1788 * new Popper(ref, pop, {
1789 * modifiers: {
1790 * preventOverflow: { enabled: false }
1791 * }
1792 * })
1793 * ```
1794 * @namespace defaults
1795 */
1796var DEFAULTS$1 = {
1797 /**
1798 * Popper's placement
1799 * @memberof defaults
1800 * @prop {String} placement='bottom'
1801 */
1802 placement: 'bottom',
1803
1804 /**
1805 * Whether events (resize, scroll) are initially enabled
1806 * @memberof defaults
1807 * @prop {Boolean} eventsEnabled=true
1808 */
1809 eventsEnabled: true,
1810
1811 /**
1812 * Set to true if you want to automatically remove the popper when
1813 * you call the `destroy` method.
1814 * @memberof defaults
1815 * @prop {Boolean} removeOnDestroy=false
1816 */
1817 removeOnDestroy: false,
1818
1819 /**
1820 * Callback called when the popper is created.
1821 * By default, is set to no-op.
1822 * Access Popper.js instance with `data.instance`.
1823 * @memberof defaults
1824 * @prop {onCreateCallback}
1825 */
1826 onCreate: function onCreate() {},
1827
1828 /**
1829 * Callback called when the popper is updated, this callback is not called
1830 * on the initialization/creation of the popper, but only on subsequent
1831 * updates.
1832 * By default, is set to no-op.
1833 * Access Popper.js instance with `data.instance`.
1834 * @memberof defaults
1835 * @prop {onUpdateCallback}
1836 */
1837 onUpdate: function onUpdate() {},
1838
1839 /**
1840 * List of modifiers used to modify the offsets before they are applied to the popper.
1841 * They provide most of the functionalities of Popper.js
1842 * @memberof defaults
1843 * @prop {modifiers}
1844 */
1845 modifiers: modifiers
1846};
1847
1848/**
1849 * Create a new Popper.js instance
1850 * @class Popper
1851 * @param {HTMLElement|referenceObject} reference - The reference element used to position the popper
1852 * @param {HTMLElement} popper - The HTML element used as popper.
1853 * @param {Object} options - Your custom options to override the ones defined in [DEFAULTS](#defaults)
1854 * @return {Object} instance - The generated Popper.js instance
1855 */
1856
1857var Popper = function () {
1858 function Popper(reference, popper) {
1859 var _this = this;
1860
1861 var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
1862 classCallCheck(this, Popper);
1863
1864 this.scheduleUpdate = function () {
1865 return requestAnimationFrame(_this.update);
1866 };
1867
1868 // make update() debounced, so that it only runs at most once-per-tick
1869 this.update = debounce(this.update.bind(this));
1870
1871 // with {} we create a new object with the options inside it
1872 this.options = _extends({}, Popper.Defaults, options);
1873
1874 // init state
1875 this.state = {
1876 isDestroyed: false,
1877 isCreated: false,
1878 scrollParents: []
1879 };
1880
1881 // get reference and popper elements (allow jQuery wrappers)
1882 this.reference = reference.jquery ? reference[0] : reference;
1883 this.popper = popper.jquery ? popper[0] : popper;
1884
1885 // make sure to apply the popper position before any computation
1886 setStyles(this.popper, { position: 'absolute' });
1887
1888 // refactoring modifiers' list (Object => Array)
1889 this.modifiers = Object.keys(Popper.Defaults.modifiers).map(function (name) {
1890 return _extends({
1891 name: name
1892 }, Popper.Defaults.modifiers[name]);
1893 });
1894
1895 // assign default values to modifiers, making sure to override them with
1896 // the ones defined by user
1897 this.modifiers = this.modifiers.map(function (defaultConfig) {
1898 var userConfig = options.modifiers && options.modifiers[defaultConfig.name] || {};
1899 return _extends({}, defaultConfig, userConfig);
1900 });
1901
1902 // add custom modifiers to the modifiers list
1903 if (options.modifiers) {
1904 this.options.modifiers = _extends({}, Popper.Defaults.modifiers, options.modifiers);
1905 Object.keys(options.modifiers).forEach(function (name) {
1906 // take in account only custom modifiers
1907 if (Popper.Defaults.modifiers[name] === undefined) {
1908 var modifier = options.modifiers[name];
1909 modifier.name = name;
1910 _this.modifiers.push(modifier);
1911 }
1912 });
1913 }
1914
1915 // sort the modifiers by order
1916 this.modifiers = this.modifiers.sort(function (a, b) {
1917 return a.order - b.order;
1918 });
1919
1920 // modifiers have the ability to execute arbitrary code when Popper.js get inited
1921 // such code is executed in the same order of its modifier
1922 // they could add new properties to their options configuration
1923 // BE AWARE: don't add options to `options.modifiers.name` but to `modifierOptions`!
1924 this.modifiers.forEach(function (modifierOptions) {
1925 if (modifierOptions.enabled && isFunction(modifierOptions.onLoad)) {
1926 modifierOptions.onLoad(_this.reference, _this.popper, _this.options, modifierOptions, _this.state);
1927 }
1928 });
1929
1930 // fire the first update to position the popper in the right place
1931 this.update();
1932
1933 var eventsEnabled = this.options.eventsEnabled;
1934 if (eventsEnabled) {
1935 // setup event listeners, they will take care of update the position in specific situations
1936 this.enableEventListeners();
1937 }
1938
1939 this.state.eventsEnabled = eventsEnabled;
1940 }
1941
1942 //
1943 // Methods
1944 //
1945
1946 /**
1947 * Updates the position of the popper, computing the new offsets and applying the new style
1948 * Prefer `scheduleUpdate` over `update` because of performance reasons
1949 * @method
1950 * @memberof Popper
1951 */
1952
1953
1954 createClass(Popper, [{
1955 key: 'update',
1956 value: function update() {
1957 // if popper is destroyed, don't perform any further update
1958 if (this.state.isDestroyed) {
1959 return;
1960 }
1961
1962 var data = {
1963 instance: this,
1964 styles: {},
1965 attributes: {},
1966 flipped: false,
1967 offsets: {}
1968 };
1969
1970 // compute reference element offsets
1971 data.offsets.reference = getReferenceOffsets(this.state, this.popper, this.reference);
1972
1973 // compute auto placement, store placement inside the data object,
1974 // modifiers will be able to edit `placement` if needed
1975 // and refer to originalPlacement to know the original value
1976 data.placement = computeAutoPlacement(this.options.placement, data.offsets.reference, this.popper, this.reference, this.options.modifiers.flip.boundariesElement);
1977
1978 // store the computed placement inside `originalPlacement`
1979 data.originalPlacement = data.placement;
1980
1981 // compute the popper offsets
1982 data.offsets.popper = getPopperOffsets(this.state, this.popper, data.offsets.reference, data.placement);
1983
1984 // run the modifiers
1985 data = runModifiers(this.modifiers, data);
1986
1987 // the first `update` will call `onCreate` callback
1988 // the other ones will call `onUpdate` callback
1989 if (!this.state.isCreated) {
1990 this.state.isCreated = true;
1991 this.options.onCreate(data);
1992 } else {
1993 this.options.onUpdate(data);
1994 }
1995 }
1996
1997 /**
1998 * Schedule an update, it will run on the next UI update available
1999 * @method scheduleUpdate
2000 * @memberof Popper
2001 */
2002
2003 }, {
2004 key: 'destroy',
2005
2006
2007 /**
2008 * Destroy the popper
2009 * @method
2010 * @memberof Popper
2011 */
2012 value: function destroy() {
2013 this.state.isDestroyed = true;
2014
2015 // touch DOM only if `applyStyle` modifier is enabled
2016 if (isModifierEnabled(this.modifiers, 'applyStyle')) {
2017 this.popper.removeAttribute('x-placement');
2018 this.popper.style.left = '';
2019 this.popper.style.position = '';
2020 this.popper.style.top = '';
2021 this.popper.style[getSupportedPropertyName('transform')] = '';
2022 }
2023
2024 this.disableEventListeners();
2025
2026 // remove the popper if user explicity asked for the deletion on destroy
2027 // do not use `remove` because IE11 doesn't support it
2028 if (this.options.removeOnDestroy) {
2029 this.popper.parentNode.removeChild(this.popper);
2030 }
2031 return this;
2032 }
2033
2034 /**
2035 * it will add resize/scroll events and start recalculating
2036 * position of the popper element when they are triggered
2037 * @method
2038 * @memberof Popper
2039 */
2040
2041 }, {
2042 key: 'enableEventListeners',
2043 value: function enableEventListeners() {
2044 if (!this.state.eventsEnabled) {
2045 this.state = setupEventListeners(this.reference, this.options, this.state, this.scheduleUpdate);
2046 }
2047 }
2048
2049 /**
2050 * it will remove resize/scroll events and won't recalculate
2051 * popper position when they are triggered. It also won't trigger onUpdate callback anymore,
2052 * unless you call 'update' method manually.
2053 * @method
2054 * @memberof Popper
2055 */
2056
2057 }, {
2058 key: 'disableEventListeners',
2059 value: function disableEventListeners() {
2060 if (this.state.eventsEnabled) {
2061 window.cancelAnimationFrame(this.scheduleUpdate);
2062 this.state = removeEventListeners(this.reference, this.state);
2063 }
2064 }
2065
2066 /**
2067 * Collection of utilities useful when writing custom modifiers.
2068 * Starting from version 1.7, this method is available only if you
2069 * include `popper-utils.js` before `popper.js`.
2070 *
2071 * **DEPRECATION**: This way to access PopperUtils is deprecated
2072 * and will be removed in v2! Use the PopperUtils module directly instead.
2073 * @static
2074 * @type {Object}
2075 * @deprecated since version 1.8
2076 * @member Utils
2077 * @memberof Popper
2078 */
2079
2080
2081 /**
2082 * List of accepted placements to use as values of the `placement` option
2083 * @static
2084 * @type {Array}
2085 * @member placements
2086 * @memberof Popper
2087 */
2088
2089
2090 /**
2091 * Default Popper.js options
2092 * @static
2093 * @type {Object}
2094 * @member Defaults
2095 * @memberof Popper
2096 */
2097
2098 }]);
2099 return Popper;
2100}();
2101
2102/**
2103 * The `referenceObject` is an object that provides an interface compatible with Popper.js
2104 * and lets you use it as replacement of a real DOM node.
2105 * You can use this method to position a popper relatively to a set of coordinates
2106 * in case you don't have a DOM node to use as reference.
2107 * NB: This feature isn't supported in Internet Explorer 10
2108 * @name referenceObject
2109 * @property {Function} data.getBoundingClientRect A function that returns a set of coordinates compatible with the native `getBoundingClientRect` method.
2110 * @property {Number} data.clientWidth An ES6 getter that will return the width of the virtual reference element.
2111 * @property {Number} data.clientHeight An ES6 getter that will return the height of the virtual reference element.
2112 */
2113
2114
2115Popper.Utils = (typeof window !== 'undefined' ? window : global).PopperUtils;
2116Popper.placements = placements;
2117Popper.Defaults = DEFAULTS$1;
2118
2119
2120//# sourceMappingURL=popper.js.map
2121
2122var classCallCheck$1 = function (instance, Constructor) {
2123 if (!(instance instanceof Constructor)) {
2124 throw new TypeError("Cannot call a class as a function");
2125 }
2126};
2127
2128var createClass$1 = function () {
2129 function defineProperties(target, props) {
2130 for (var i = 0; i < props.length; i++) {
2131 var descriptor = props[i];
2132 descriptor.enumerable = descriptor.enumerable || false;
2133 descriptor.configurable = true;
2134 if ("value" in descriptor) descriptor.writable = true;
2135 Object.defineProperty(target, descriptor.key, descriptor);
2136 }
2137 }
2138
2139 return function (Constructor, protoProps, staticProps) {
2140 if (protoProps) defineProperties(Constructor.prototype, protoProps);
2141 if (staticProps) defineProperties(Constructor, staticProps);
2142 return Constructor;
2143 };
2144}();
2145
2146
2147
2148
2149
2150
2151
2152var _extends$1 = Object.assign || function (target) {
2153 for (var i = 1; i < arguments.length; i++) {
2154 var source = arguments[i];
2155
2156 for (var key in source) {
2157 if (Object.prototype.hasOwnProperty.call(source, key)) {
2158 target[key] = source[key];
2159 }
2160 }
2161 }
2162
2163 return target;
2164};
2165
2166/**!
2167* @file tippy.js | Pure JS Tooltip Library
2168* @version 0.11.0
2169* @license MIT
2170*/
2171
2172// Touch user is assumed false until a `touchstart` event is fired
2173// id counter for our aria-describedby labelling (tooltip IDs)
2174var GLOBALS = {
2175 touchUser: false,
2176 idCounter: 0
2177};
2178
2179// Storage object to hold all references from instance instantiation
2180// Allows us to hide tooltips from other instances when clicking on the body
2181var STORE = {
2182 refs: [],
2183 els: [],
2184 poppers: []
2185};
2186
2187var DEFAULTS = {
2188 html: false,
2189 position: 'top',
2190 animation: 'shift',
2191 animateFill: true,
2192 arrow: false,
2193 arrowSize: 'regular',
2194 delay: 0,
2195 hideDelay: 0,
2196 trigger: 'mouseenter focus',
2197 duration: 375,
2198 hideDuration: 375,
2199 interactive: false,
2200 theme: 'dark',
2201 size: 'regular',
2202 distance: 10,
2203 offset: 0,
2204 hideOnClick: true,
2205 multiple: false,
2206 followCursor: false,
2207 inertia: false,
2208 transitionFlip: true,
2209 popperOptions: {}
2210};
2211
2212var DEFAULTS_KEYS = Object.keys(DEFAULTS);
2213
2214var SELECTORS = {
2215 popper: '.tippy-popper',
2216 tooltip: '.tippy-tooltip',
2217 content: '.tippy-tooltip-content',
2218 circle: '[x-circle]',
2219 arrow: '[x-arrow]',
2220 el: '[data-tooltipped]',
2221 controller: '[data-tippy-controller]'
2222};
2223
2224// Determine touch users
2225function handleDocumentTouchstart() {
2226 GLOBALS.touchUser = true;
2227 document.body.classList.add('tippy-touch');
2228 document.removeEventListener('touchstart', handleDocumentTouchstart);
2229}
2230
2231// Handle clicks anywhere on the document
2232function handleDocumentClick(event) {
2233
2234 var el = closest(event.target, SELECTORS.el);
2235 var popper = closest(event.target, SELECTORS.popper);
2236
2237 if (popper) {
2238 var ref = STORE.refs[STORE.poppers.indexOf(popper)];
2239 if (ref.settings.interactive) return;
2240 }
2241
2242 if (el) {
2243 var _ref = STORE.refs[STORE.els.indexOf(el)];
2244
2245 // Hide all poppers except the one belonging to the element that was clicked IF
2246 // `multiple` is false AND they are a touch user, OR
2247 // `multiple` is false AND it's triggered by a click
2248 if (!_ref.settings.multiple && GLOBALS.touchUser || !_ref.settings.multiple && _ref.settings.trigger.indexOf('click') !== -1) {
2249 return hideAllPoppers(_ref);
2250 }
2251
2252 // If hideOnClick is not strictly true or triggered by a click don't hide poppers
2253 if (_ref.settings.hideOnClick !== true || _ref.settings.trigger.indexOf('click') !== -1) return;
2254 }
2255
2256 // Don't trigger a hide for tippy controllers, and don't needlessly run loop
2257 if (!closest(event.target, SELECTORS.controller) && document.body.querySelector(SELECTORS.popper)) {
2258 hideAllPoppers();
2259 }
2260}
2261
2262document.addEventListener('click', handleDocumentClick);
2263document.addEventListener('touchstart', handleDocumentTouchstart);
2264
2265/**
2266* Returns the supported prefixed property - only `webkit` is needed, `moz`, `ms` and `o` are obsolete
2267* @param {String} property
2268* @return {String} - browser supported prefixed property
2269*/
2270function prefix(property) {
2271 var prefixes = [false, 'webkit'];
2272 var upperProp = property.charAt(0).toUpperCase() + property.slice(1);
2273
2274 for (var i = 0; i < prefixes.length; i++) {
2275 var _prefix = prefixes[i];
2276 var prefixedProp = _prefix ? '' + _prefix + upperProp : property;
2277 if (typeof window.document.body.style[prefixedProp] !== 'undefined') {
2278 return prefixedProp;
2279 }
2280 }
2281
2282 return null;
2283}
2284
2285/**
2286* Returns the non-shifted placement (e.g., 'bottom-start' => 'bottom')
2287* @param {String} str - placement
2288* @return {String}
2289*/
2290function getCorePlacement(str) {
2291 return str.replace(/-.+/, '');
2292}
2293
2294/**
2295* Polyfill to get the closest parent element
2296* @param {Element} element - child of parent to be returned
2297* @param {String} parentSelector - selector to match the parent if found
2298* @return {Element}
2299*/
2300function closest(element, parentSelector) {
2301 if (!Element.prototype.matches) {
2302 Element.prototype.matches = Element.prototype.matchesSelector || Element.prototype.webkitMatchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || function (s) {
2303 var matches = (this.document || this.ownerDocument).querySelectorAll(s),
2304 i = matches.length;
2305 while (--i >= 0 && matches.item(i) !== this) {}
2306 return i > -1;
2307 };
2308 }
2309 if (!Element.prototype.closest) Element.prototype.closest = function (selector) {
2310 var el = this;
2311 while (el) {
2312 if (el.matches(selector)) {
2313 return el;
2314 }
2315 el = el.parentElement;
2316 }
2317 };
2318 return element.closest(parentSelector);
2319}
2320
2321/**
2322* Creates a new popper instance
2323* @param {Object} ref
2324* @return {Object} - the popper instance
2325*/
2326function createPopperInstance(ref) {
2327
2328 var settings = ref.settings;
2329 var tooltip = ref.popper.querySelector(SELECTORS.tooltip);
2330
2331 var config = _extends$1({
2332 placement: settings.position
2333 }, settings.popperOptions || {}, {
2334 modifiers: _extends$1({}, settings.popperOptions ? settings.popperOptions.modifiers : {}, {
2335 flip: _extends$1({
2336 padding: parseInt(settings.distance) + 3 /* 3px from viewport boundary */
2337 }, settings.popperOptions && settings.popperOptions.modifiers ? settings.popperOptions.modifiers.flip : {}),
2338 offset: _extends$1({
2339 offset: parseInt(settings.offset)
2340 }, settings.popperOptions && settings.popperOptions.modifiers ? settings.popperOptions.modifiers.offset : {})
2341 }),
2342 onUpdate: function onUpdate() {
2343 tooltip.style.top = '';
2344 tooltip.style.bottom = '';
2345 tooltip.style.left = '';
2346 tooltip.style.right = '';
2347 tooltip.style[getCorePlacement(ref.popper.getAttribute('x-placement'))] = -(settings.distance - 10) + 'px';
2348 }
2349 });
2350
2351 return new Popper(ref.el, ref.popper, config);
2352}
2353
2354/**
2355* Creates a popper element then returns it
2356* @param {String} title - the tooltip's `title` attribute
2357* @param {Object} settings - individual settings
2358* @return {Element} - the popper element
2359*/
2360function createPopperElement(id, title, settings) {
2361 var popper = document.createElement('div');
2362 popper.setAttribute('class', 'tippy-popper');
2363 popper.setAttribute('role', 'tooltip');
2364 popper.setAttribute('aria-hidden', 'true');
2365 popper.setAttribute('id', 'tippy-tooltip-' + id);
2366
2367 var tooltip = document.createElement('div');
2368 tooltip.setAttribute('class', 'tippy-tooltip tippy-tooltip--' + settings.size + ' ' + settings.theme + '-theme leave');
2369 tooltip.setAttribute('data-animation', settings.animation);
2370
2371 if (settings.arrow) {
2372 // Add an arrow
2373 var arrow = document.createElement('div');
2374 arrow.setAttribute('class', 'arrow-' + settings.arrowSize);
2375 arrow.setAttribute('x-arrow', '');
2376 tooltip.appendChild(arrow);
2377 }
2378
2379 if (settings.animateFill) {
2380 // Create animateFill circle element for animation
2381 tooltip.setAttribute('data-animatefill', '');
2382 var circle = document.createElement('div');
2383 circle.setAttribute('class', 'leave');
2384 circle.setAttribute('x-circle', '');
2385 tooltip.appendChild(circle);
2386 }
2387
2388 if (settings.inertia) {
2389 // Change transition timing function cubic bezier
2390 tooltip.setAttribute('data-inertia', '');
2391 }
2392
2393 // Tooltip content (text or HTML)
2394 var content = document.createElement('div');
2395 content.setAttribute('class', 'tippy-tooltip-content');
2396
2397 if (settings.html) {
2398
2399 var templateId = void 0;
2400
2401 if (settings.html instanceof Element) {
2402 content.innerHTML = settings.html.innerHTML;
2403 templateId = settings.html.id || 'tippy-html-template';
2404 } else {
2405 content.innerHTML = document.getElementById(settings.html.replace('#', '')).innerHTML;
2406 templateId = settings.html;
2407 }
2408
2409 popper.classList.add('html-template');
2410 popper.setAttribute('tabindex', '0');
2411 tooltip.setAttribute('data-template-id', templateId);
2412 } else {
2413 content.innerHTML = title;
2414 }
2415
2416 // Init distance. Further updates are made in the popper instance's `onUpdate()` method
2417 tooltip.style[getCorePlacement(settings.position)] = -(settings.distance - 10) + 'px';
2418
2419 tooltip.appendChild(content);
2420 popper.appendChild(tooltip);
2421
2422 return popper;
2423}
2424
2425/**
2426* Creates a trigger for each one specified
2427* @param {Object} event - the custom event specified in the `trigger` setting
2428* @param {Element} el
2429* @param {Object} methods - the methods for each listener
2430* @return {Array} - array of listener objects
2431*/
2432function createTrigger(event, el, handlers) {
2433 var listeners = [];
2434
2435 if (event === 'manual') return listeners;
2436
2437 // Enter
2438 el.addEventListener(event, handlers.handleTrigger);
2439 listeners.push({
2440 event: event,
2441 handler: handlers.handleTrigger
2442 });
2443
2444 // Leave
2445 if (event === 'mouseenter') {
2446 el.addEventListener('mouseleave', handlers.handleMouseleave);
2447 listeners.push({
2448 event: 'mouseleave',
2449 handler: handlers.handleMouseleave
2450 });
2451 }
2452 if (event === 'focus') {
2453 el.addEventListener('blur', handlers.handleBlur);
2454 listeners.push({
2455 event: 'blur',
2456 handler: handlers.handleBlur
2457 });
2458 }
2459
2460 return listeners;
2461}
2462
2463/**
2464* Adds each reference (tooltipped element, popper and its settings/listeners etc)
2465* into global storage
2466* @param {Object} ref - current ref in the forEach loop to be pushed
2467*/
2468function pushIntoStorage(ref) {
2469 STORE.refs.push(ref);
2470 STORE.els.push(ref.el);
2471 STORE.poppers.push(ref.popper);
2472}
2473
2474/**
2475* Removes the title from the tooltipped element
2476* @param {Element} el
2477*/
2478function removeTitle(el) {
2479 var title = el.getAttribute('title');
2480 el.setAttribute('data-original-title', title || 'html');
2481 el.removeAttribute('title');
2482}
2483
2484/**
2485* Determines if an element is visible in the viewport
2486* @param {Element} el
2487* @return {Boolean}
2488*/
2489function elementIsInViewport(el) {
2490 var rect = el.getBoundingClientRect();
2491
2492 return rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth);
2493}
2494
2495/**
2496* Mousemove event listener callback method for follow cursor setting
2497* @param {Object} e (event)
2498*/
2499function followCursor(e) {
2500 var ref = STORE.refs[STORE.els.indexOf(this)];
2501 var position = getCorePlacement(ref.popper.getAttribute('x-placement'));
2502 var halfPopperWidth = Math.round(ref.popper.offsetWidth / 2);
2503 var halfPopperHeight = Math.round(ref.popper.offsetHeight / 2);
2504
2505 // Default = top
2506 var x = e.pageX - halfPopperWidth;
2507 var y = e.pageY - 2.5 * halfPopperHeight;
2508
2509 if (position === 'left') {
2510 x = e.pageX - 2 * halfPopperWidth - 15;
2511 y = e.pageY - halfPopperHeight;
2512 } else if (position === 'right') {
2513 x = e.pageX + halfPopperHeight;
2514 y = e.pageY - halfPopperHeight;
2515 } else if (position === 'bottom') {
2516 y = e.pageY + halfPopperHeight / 1.5;
2517 }
2518
2519 ref.popper.style[prefix('transform')] = 'translate3d(' + x + 'px, ' + y + 'px, 0)';
2520}
2521
2522/**
2523* Triggers a document repaint or reflow for CSS transition
2524* @param {Element} el
2525*/
2526function triggerReflow(tooltip, circle) {
2527 // Safari needs the specific 'transform' property to be accessed
2528 circle ? window.getComputedStyle(circle)[prefix('transform')] : window.getComputedStyle(tooltip).opacity;
2529}
2530
2531/**
2532* Modifies elements' class lists
2533* @param {Array} els - HTML elements
2534* @param {Function} callback
2535*/
2536function modifyClassList(els, callback) {
2537 els.forEach(function (el) {
2538 if (!el) return;
2539 callback(el.classList);
2540 });
2541}
2542
2543/**
2544* Applies the transition duration to each element
2545* @param {Array} els - HTML elements
2546* @param {Number} duration
2547*/
2548function applyTransitionDuration(els, duration) {
2549 els.forEach(function (el) {
2550 if (!el) return;
2551 if (el.hasAttribute('x-circle')) duration = Math.round(duration / 1.25);
2552 el.style[prefix('transitionDuration')] = duration + 'ms';
2553 });
2554}
2555
2556/**
2557* Fixes CSS transition
2558* @param {Object} ref - element/popper reference
2559* @param {Function} callback - the quick hide/show correction
2560*/
2561function correctTransition(ref, callback) {
2562 setTimeout(function () {
2563 if (ref.settings.position !== ref.popper.getAttribute('x-placement')) {
2564 ref.flipped = true;
2565 callback();
2566 } else if (ref.flipped && ref.settings.position === ref.popper.getAttribute('x-placement')) {
2567 ref.flipped = false;
2568 callback();
2569 }
2570 }, 0);
2571}
2572
2573/**
2574* Prepares the callback functions for `show` and `hide` methods
2575* @param {Object} ref - the element/popper reference
2576* @param {Function} callback - callback function to fire once transitions complete
2577*/
2578function onTransitionEnd(ref, callback) {
2579 var tooltip = ref.popper.querySelector(SELECTORS.tooltip);
2580
2581 var listenerCallback = function listenerCallback() {
2582 tooltip.removeEventListener('webkitTransitionEnd', listenerCallback);
2583 tooltip.removeEventListener('transitionend', listenerCallback);
2584 callback();
2585 };
2586
2587 // Wait for transitions to complete
2588 tooltip.addEventListener('webkitTransitionEnd', listenerCallback);
2589 tooltip.addEventListener('transitionend', listenerCallback);
2590}
2591
2592/**
2593* Creates a popper instance if one does not exist, then appends the popper
2594* Also updates its position if need be and enables event listeners
2595* @param {Object} ref - the element/popper reference
2596*/
2597function awakenPopper(ref) {
2598 document.body.appendChild(ref.popper);
2599
2600 if (ref.settings.followCursor && !ref.hasFollowCursorListener && !GLOBALS.touchUser) {
2601 ref.hasFollowCursorListener = true;
2602 ref.el.addEventListener('mousemove', followCursor);
2603 }
2604
2605 if (!ref.popperInstance) {
2606 // Create instance if it hasn't been created yet
2607 ref.popperInstance = createPopperInstance(ref);
2608 if (ref.settings.followCursor && !GLOBALS.touchUser) {
2609 ref.popperInstance.disableEventListeners();
2610 }
2611 } else {
2612 ref.popperInstance.update();
2613 if (!ref.settings.followCursor) {
2614 ref.popperInstance.enableEventListeners();
2615 }
2616 }
2617}
2618
2619/**
2620* Hides all poppers
2621* @param {Object} - currentRef
2622*/
2623function hideAllPoppers(currentRef) {
2624 STORE.refs.forEach(function (ref) {
2625 // Don't hide already hidden ones
2626 if (!document.body.contains(ref.popper)) return;
2627
2628 // hideOnClick can have the truthy value of 'persistent', so strict check is needed
2629 if (ref.settings.hideOnClick === true && (!currentRef || ref.popper !== currentRef.popper)) {
2630 ref.tippyInstance.hide(ref.popper, ref.settings.hideDuration);
2631 }
2632 });
2633}
2634
2635/**
2636* The class to be exported to be used on the `window`
2637* Private methods are prefixed with an underscore _
2638* @param {String|Element} selector
2639* @param {Object} settings (optional) - the object of settings to be applied to the instance
2640*/
2641
2642var Tippy$1 = function () {
2643 function Tippy(selector) {
2644 var settings = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2645 classCallCheck$1(this, Tippy);
2646
2647
2648 // Use default browser tooltip on old browsers (IE < 10) and Opera Mini
2649 if (!('addEventListener' in window) || /MSIE 9/i.test(navigator.userAgent) || window.operamini) return;
2650
2651 this.settings = _extends$1(JSON.parse(JSON.stringify(DEFAULTS)), settings);
2652
2653 this.callbacks = {
2654 wait: settings.wait,
2655 beforeShown: settings.beforeShown || new Function(),
2656 shown: settings.shown || new Function(),
2657 beforeHidden: settings.beforeHidden || new Function(),
2658 hidden: settings.hidden || new Function()
2659 };
2660
2661 // Check if selector is a DOM element
2662 var els = selector instanceof Element ? [selector] : [].slice.call(document.querySelectorAll(selector));
2663
2664 this._createTooltips(els);
2665 }
2666
2667 /**
2668 * Returns an object of settings to override global settings
2669 * @param {Element} el - the tooltipped element
2670 * @return {Object} - individual settings
2671 */
2672
2673
2674 createClass$1(Tippy, [{
2675 key: '_applyIndividualSettings',
2676 value: function _applyIndividualSettings(el) {
2677 var _this = this;
2678
2679 var settings = {};
2680
2681 DEFAULTS_KEYS.forEach(function (key) {
2682 var val = el.getAttribute('data-' + key.toLowerCase()) || _this.settings[key];
2683 if (val === 'false') val = false;
2684
2685 settings[key] = val;
2686 });
2687
2688 // animateFill is disabled if an arrow is true
2689 if (settings.arrow) settings['animateFill'] = false;
2690
2691 return _extends$1(_extends$1({}, this.settings), settings);
2692 }
2693
2694 /**
2695 * Returns relevant listener callbacks for each ref
2696 * @param {Element} el
2697 * @param {Element} popper
2698 * @param {Object} settings
2699 * @return {Object} - relevant listener callback methods
2700 */
2701
2702 }, {
2703 key: '_getEventListenerHandlers',
2704 value: function _getEventListenerHandlers(el, popper, settings) {
2705 var _this2 = this;
2706
2707 // Avoid creating unnecessary timeouts
2708 var _show = function _show() {
2709 clearTimeout(popper.getAttribute('data-delay'));
2710 clearTimeout(popper.getAttribute('data-hidedelay'));
2711
2712 // Already visible. For clicking when it also has a `focus` event listener
2713 if (popper.style.visibility === 'visible') return;
2714
2715 if (settings.delay) {
2716 var delay = setTimeout(function () {
2717 return _this2.show(popper, settings.duration);
2718 }, settings.delay);
2719 popper.setAttribute('data-delay', delay);
2720 } else {
2721 _this2.show(popper, settings.duration);
2722 }
2723 };
2724
2725 var show = function show() {
2726 return _this2.callbacks.wait ? _this2.callbacks.wait(_show) : _show();
2727 };
2728
2729 var hide = function hide() {
2730 clearTimeout(popper.getAttribute('data-hidedelay'));
2731 clearTimeout(popper.getAttribute('data-delay'));
2732
2733 if (settings.hideDelay) {
2734 var delay = setTimeout(function () {
2735 return _this2.hide(popper, settings.hideDuration);
2736 }, settings.hideDelay);
2737 popper.setAttribute('data-hidedelay', delay);
2738 } else {
2739 _this2.hide(popper, settings.hideDuration);
2740 }
2741 };
2742
2743 var handleTrigger = function handleTrigger(event) {
2744 // Toggle show/hide when clicking click-triggered tooltips
2745 if (event.type === 'click' && popper.style.visibility === 'visible' && settings.hideOnClick !== 'persistent') {
2746 return hide();
2747 }
2748
2749 show();
2750 };
2751
2752 var handleMouseleave = function handleMouseleave(event) {
2753
2754 if (settings.interactive) {
2755 // Temporarily handle mousemove to check if the mouse left somewhere
2756 // other than its popper
2757 var handleMousemove = function handleMousemove(event) {
2758 // If cursor is NOT on the popper
2759 // and it's NOT on the popper's tooltipped element
2760 // and it's NOT triggered by a click, then hide
2761 if (closest(event.target, SELECTORS.popper) !== popper && closest(event.target, SELECTORS.el) !== el && settings.trigger.indexOf('click') === -1) {
2762 document.removeEventListener('mousemove', handleMousemove);
2763 hide();
2764 }
2765 };
2766 return document.addEventListener('mousemove', handleMousemove);
2767 }
2768
2769 // If it's not interactive, just hide it
2770 hide();
2771 };
2772
2773 var handleBlur = function handleBlur(event) {
2774 // Only hide if not a touch user and has a focus 'relatedtarget', of which is not
2775 // a popper element
2776 if (!GLOBALS.touchUser && event.relatedTarget) {
2777 if (!closest(event.relatedTarget, SELECTORS.popper)) {
2778 hide();
2779 }
2780 }
2781 };
2782
2783 return {
2784 handleTrigger: handleTrigger,
2785 handleMouseleave: handleMouseleave,
2786 handleBlur: handleBlur
2787 };
2788 }
2789
2790 /**
2791 * Creates tooltips for all elements that match the instance's selector
2792 */
2793
2794 }, {
2795 key: '_createTooltips',
2796 value: function _createTooltips(els) {
2797 var _this3 = this;
2798
2799 els.forEach(function (el) {
2800 var settings = _this3._applyIndividualSettings(el);
2801
2802 var title = el.getAttribute('title');
2803 if (!title && !settings.html) return;
2804
2805 var id = GLOBALS.idCounter;
2806 el.setAttribute('data-tooltipped', '');
2807 el.setAttribute('aria-describedby', 'tippy-tooltip-' + id);
2808
2809 removeTitle(el);
2810
2811 var popper = createPopperElement(id, title, settings);
2812 var handlers = _this3._getEventListenerHandlers(el, popper, settings);
2813 var listeners = [];
2814
2815 settings.trigger.trim().split(' ').forEach(function (event) {
2816 return listeners = listeners.concat(createTrigger(event, el, handlers));
2817 });
2818
2819 pushIntoStorage({
2820 id: id,
2821 el: el,
2822 popper: popper,
2823 settings: settings,
2824 listeners: listeners,
2825 tippyInstance: _this3
2826 });
2827
2828 GLOBALS.idCounter++;
2829 });
2830
2831 Tippy.store = STORE; // Allow others to access `STORE` if need be
2832 }
2833
2834 /**
2835 * Returns a tooltipped element's popper reference
2836 * @param {Element} el
2837 * @return {Element}
2838 */
2839
2840 }, {
2841 key: 'getPopperElement',
2842 value: function getPopperElement(el) {
2843 try {
2844 return STORE.refs[STORE.els.indexOf(el)].popper;
2845 } catch (e) {
2846 throw new Error('[Tippy error]: Element does not exist in any Tippy instances');
2847 }
2848 }
2849
2850 /**
2851 * Returns a popper's tooltipped element reference
2852 * @param {Element} popper
2853 * @return {Element}
2854 */
2855
2856 }, {
2857 key: 'getTooltippedElement',
2858 value: function getTooltippedElement(popper) {
2859 try {
2860 return STORE.refs[STORE.poppers.indexOf(popper)].el;
2861 } catch (e) {
2862 throw new Error('[Tippy error]: Popper does not exist in any Tippy instances');
2863 }
2864 }
2865
2866 /**
2867 * Shows a popper
2868 * @param {Element} popper
2869 * @param {Number} duration (optional)
2870 * @param {Boolean} enableCallback (optional)
2871 */
2872
2873 }, {
2874 key: 'show',
2875 value: function show(popper) {
2876 var _this4 = this;
2877
2878 var duration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.settings.duration;
2879 var enableCallback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
2880
2881 var ref = STORE.refs[STORE.poppers.indexOf(popper)];
2882 var tooltip = popper.querySelector(SELECTORS.tooltip);
2883 var circle = popper.querySelector(SELECTORS.circle);
2884
2885 if (enableCallback) {
2886 this.callbacks.beforeShown();
2887
2888 // Flipping causes CSS transition to go haywire
2889 if (duration >= 20) {
2890 correctTransition(ref, function () {
2891 _this4.hide(popper, 0, false);
2892 setTimeout(function () {
2893 // Under fast-moving cursor cases, the tooltip can stay stuck because
2894 // the mouseleave triggered before this show
2895 // hidden only becomes `true` in the `hide` method if callback is enabled
2896 // (i.e. legitimate hide, not triggered by this correcttransition function)
2897 if (ref.hidden) return;
2898
2899 _this4.show(popper, duration, false);
2900 }, 0);
2901 });
2902 }
2903 }
2904
2905 if (!document.body.contains(popper)) {
2906 awakenPopper(ref);
2907 }
2908
2909 // Interactive tooltips receive a class of 'active'
2910 if (ref.settings.interactive) {
2911 ref.el.classList.add('active');
2912 }
2913
2914 ref.hidden = false;
2915 ref.popper.style.visibility = 'visible';
2916 ref.popper.setAttribute('aria-hidden', 'false');
2917
2918 // Repaint/reflow is required for CSS transition when appending
2919 triggerReflow(tooltip, circle);
2920
2921 modifyClassList([tooltip, circle], function (list) {
2922 list.remove('leave');
2923 list.add('enter');
2924 });
2925
2926 applyTransitionDuration([tooltip, circle], duration);
2927
2928 // Wait for transitions to complete
2929
2930 var transitionendFired = false;
2931
2932 var transitionendCallback = function transitionendCallback() {
2933 transitionendFired = true;
2934
2935 if (popper.style.visibility === 'hidden' || ref.onShownFired) return;
2936
2937 if (!ref.settings.transitionFlip) {
2938 tooltip.classList.add('tippy-notransition');
2939 }
2940
2941 // Focus interactive tooltips only
2942 if (ref.settings.interactive) {
2943 popper.focus();
2944 }
2945
2946 // Prevents shown() from firing more than once from early transition cancellations
2947 ref.onShownFired = true;
2948
2949 if (enableCallback) _this4.callbacks.shown();
2950 };
2951
2952 onTransitionEnd(ref, transitionendCallback);
2953
2954 // transitionend listener sometimes may not fire
2955 clearTimeout(ref.transitionendTimeout);
2956 ref.transitionendTimeout = setTimeout(function () {
2957 if (!transitionendFired) {
2958 transitionendCallback();
2959 }
2960 }, duration);
2961 }
2962
2963 /**
2964 * Hides a popper
2965 * @param {Element} popper
2966 * @param {Number} duration (optional)
2967 * @param {Boolean} enableCallback (optional)
2968 */
2969
2970 }, {
2971 key: 'hide',
2972 value: function hide(popper) {
2973 var _this5 = this;
2974
2975 var duration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.settings.duration;
2976 var enableCallback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
2977
2978 var ref = STORE.refs[STORE.poppers.indexOf(popper)];
2979 var tooltip = popper.querySelector(SELECTORS.tooltip);
2980 var circle = popper.querySelector(SELECTORS.circle);
2981 var content = popper.querySelector(SELECTORS.content);
2982
2983 if (enableCallback) {
2984 this.callbacks.beforeHidden();
2985
2986 // flag needed for correctTransition, popper.style.visibility must be used by
2987 // correctTransition
2988 ref.hidden = true;
2989
2990 ref.el.classList.remove('active');
2991
2992 ref.onShownFired = false;
2993
2994 if (!ref.settings.transitionFlip) {
2995 tooltip.classList.remove('tippy-notransition');
2996 }
2997
2998 ref.flipped = ref.settings.position !== popper.getAttribute('x-placement');
2999 }
3000
3001 popper.style.visibility = 'hidden';
3002 popper.setAttribute('aria-hidden', 'true');
3003
3004 // Use same duration as show if it's the default
3005 if (duration === DEFAULTS.hideDuration) {
3006 duration = parseInt(tooltip.style[prefix('transitionDuration')]);
3007 } else {
3008 applyTransitionDuration([tooltip, circle], duration);
3009 }
3010
3011 modifyClassList([tooltip, circle], function (list) {
3012 list.remove('enter');
3013 list.add('leave');
3014 });
3015
3016 // Re-focus tooltipped element if it's a HTML popover
3017 // and the tooltipped element IS in the viewport (otherwise it causes unsightly scrolling
3018 // if the tooltip is closed and the element isn't in the viewport anymore)
3019 if (ref.settings.html && ref.settings.trigger.indexOf('click') !== -1 && elementIsInViewport(ref.el)) {
3020 ref.el.focus();
3021 }
3022
3023 // Wait for transitions to complete
3024
3025 var transitionendFired = false;
3026
3027 var transitionendCallback = function transitionendCallback() {
3028 transitionendFired = true;
3029
3030 if (popper.style.visibility === 'visible' || !document.body.contains(popper)) return;
3031
3032 ref.popperInstance.disableEventListeners();
3033
3034 document.body.removeChild(popper);
3035
3036 if (enableCallback) _this5.callbacks.hidden();
3037 };
3038
3039 onTransitionEnd(ref, transitionendCallback);
3040
3041 // transitionend listener sometimes may not fire
3042 clearTimeout(ref.transitionendTimeout);
3043 ref.transitionendTimeout = setTimeout(function () {
3044 if (!transitionendFired) {
3045 transitionendCallback();
3046 }
3047 }, duration);
3048 }
3049
3050 /**
3051 * Destroys a popper
3052 * @param {Element} popper
3053 */
3054
3055 }, {
3056 key: 'destroy',
3057 value: function destroy(popper) {
3058 var index = STORE.poppers.indexOf(popper);
3059 var ref = STORE.refs[index];
3060
3061 // Remove Tippy-only event listeners from tooltipped element
3062 ref.listeners.forEach(function (listener) {
3063 return ref.el.removeEventListener(listener.event, listener.handler);
3064 });
3065
3066 ref.el.removeAttribute('data-tooltipped');
3067 ref.el.removeAttribute('aria-describedby');
3068
3069 if (ref.popperInstance) {
3070 ref.popperInstance.destroy();
3071 }
3072
3073 // Remove from storage
3074 STORE.refs.splice(index, 1);
3075 STORE.els.splice(index, 1);
3076 STORE.poppers.splice(index, 1);
3077 }
3078
3079 /**
3080 * Updates a popper with new content
3081 * @param {Element} popper
3082 */
3083
3084 }, {
3085 key: 'update',
3086 value: function update(popper) {
3087 var ref = STORE.refs[STORE.poppers.indexOf(popper)];
3088 var content = popper.querySelector(SELECTORS.content);
3089 var template = ref.settings.html;
3090
3091 if (template) {
3092 content.innerHTML = template instanceof Element ? template.innerHTML : document.getElementById(template.replace('#', '')).innerHTML;
3093 } else {
3094 content.innerHTML = ref.el.getAttribute('title') || ref.el.getAttribute('data-original-title');
3095 removeTitle(ref.el);
3096 }
3097 }
3098 }]);
3099 return Tippy;
3100}();
3101
3102return Tippy$1;
3103
3104})));