/**!
 * @fileOverview Kickass library to create and place poppers near their reference elements.
 * @version 1.3.1
 * @license
 * Copyright (c) 2016 Federico Zivolo and contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('popper.js')) :
        typeof define === 'function' && define.amd ? define(['popper.js'], factory) :
            (global.Tooltip = factory(global.Popper));
}(this, (function (Popper) {
    'use strict';

    Popper = Popper && Popper.hasOwnProperty('default') ? Popper['default'] : Popper;

    /**
     * Check if the given variable is a function
     * @method
     * @memberof Popper.Utils
     * @argument {Any} functionToCheck - variable to check
     * @returns {Boolean} answer to: is a function?
     */
    function isFunction(functionToCheck) {
        var getType = {};
        return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
    }

    var classCallCheck = function (instance, Constructor) {
        if (!(instance instanceof Constructor)) {
            throw new TypeError("Cannot call a class as a function");
        }
    };

    var createClass = function () {
        function defineProperties(target, props) {
            for (var i = 0; i < props.length; i++) {
                var descriptor = props[i];
                descriptor.enumerable = descriptor.enumerable || false;
                descriptor.configurable = true;
                if ("value" in descriptor) descriptor.writable = true;
                Object.defineProperty(target, descriptor.key, descriptor);
            }
        }

        return function (Constructor, protoProps, staticProps) {
            if (protoProps) defineProperties(Constructor.prototype, protoProps);
            if (staticProps) defineProperties(Constructor, staticProps);
            return Constructor;
        };
    }();


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

            for (var key in source) {
                if (Object.prototype.hasOwnProperty.call(source, key)) {
                    target[key] = source[key];
                }
            }
        }

        return target;
    };

    var DEFAULT_OPTIONS = {
        container: false,
        delay: 0,
        html: false,
        placement: 'top',
        title: '',
        template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
        trigger: 'hover focus',
        offset: 0,
        arrowSelector: '.tooltip-arrow, .tooltip__arrow',
        innerSelector: '.tooltip-inner, .tooltip__inner'
    };

    var Tooltip = function () {
        /**
         * Create a new Tooltip.js instance
         * @class Tooltip
         * @param {HTMLElement} reference - The DOM node used as reference of the tooltip (it can be a jQuery element).
         * @param {Object} options
         * @param {String} options.placement='top'
         *      Placement of the popper accepted values: `top(-start, -end), right(-start, -end), bottom(-start, -end),
         *      left(-start, -end)`
         * @param {String} options.arrowSelector='.tooltip-arrow, .tooltip__arrow' - className used to locate the DOM arrow element in the tooltip.
         * @param {String} options.innerSelector='.tooltip-inner, .tooltip__inner' - className used to locate the DOM inner element in the tooltip.
         * @param {HTMLElement|String|false} options.container=false - Append the tooltip to a specific element.
         * @param {Number|Object} options.delay=0
         *      Delay showing and hiding the tooltip (ms) - does not apply to manual trigger type.
         *      If a number is supplied, delay is applied to both hide/show.
         *      Object structure is: `{ show: 500, hide: 100 }`
         * @param {Boolean} options.html=false - Insert HTML into the tooltip. If false, the content will inserted with `textContent`.
         * @param {String} [options.template='<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>']
         *      Base HTML to used when creating the tooltip.
         *      The tooltip's `title` will be injected into the `.tooltip-inner` or `.tooltip__inner`.
         *      `.tooltip-arrow` or `.tooltip__arrow` will become the tooltip's arrow.
         *      The outermost wrapper element should have the `.tooltip` class.
         * @param {String|HTMLElement|TitleFunction} options.title='' - Default title value if `title` attribute isn't present.
         * @param {String} [options.trigger='hover focus']
         *      How tooltip is triggered - click, hover, focus, manual.
         *      You may pass multiple triggers; separate them with a space. `manual` cannot be combined with any other trigger.
         * @param {Boolean} options.closeOnClickOutside=false - Close a popper on click outside of the popper and reference element. This has effect only when options.trigger is 'click'.
         * @param {String|HTMLElement} options.boundariesElement
         *      The element used as boundaries for the tooltip. For more information refer to Popper.js'
         *      [boundariesElement docs](https://popper.js.org/popper-documentation.html)
         * @param {Number|String} options.offset=0 - Offset of the tooltip relative to its reference. For more information refer to Popper.js'
         *      [offset docs](https://popper.js.org/popper-documentation.html)
         * @param {Object} options.popperOptions={} - Popper options, will be passed directly to popper instance. For more information refer to Popper.js'
         *      [options docs](https://popper.js.org/popper-documentation.html)
         * @return {Object} instance - The generated tooltip instance
         */
        function Tooltip(reference, options) {
            classCallCheck(this, Tooltip);

            _initialiseProps.call(this);

            // apply user options over default ones
            options = _extends({}, DEFAULT_OPTIONS, options);

            reference.jquery && (reference = reference[0]);

            // cache reference and options
            this.reference = reference;
            this.options = options;

            // get events list
            var events = typeof options.trigger === 'string' ? options.trigger.split(' ').filter(function (trigger) {
                return ['click', 'hover', 'focus'].indexOf(trigger) !== -1;
            }) : [];

            // set initial state
            this._isOpen = false;
            this._popperOptions = {};

            // set event listeners
            this._setEventListeners(reference, events, options);
        }

        //
        // Public methods
        //

        /**
         * Reveals an element's tooltip. This is considered a "manual" triggering of the tooltip.
         * Tooltips with zero-length titles are never displayed.
         * @method Tooltip#show
         * @memberof Tooltip
         */


        /**
         * Hides an element’s tooltip. This is considered a “manual” triggering of the tooltip.
         * @method Tooltip#hide
         * @memberof Tooltip
         */


        /**
         * Hides and destroys an element’s tooltip.
         * @method Tooltip#dispose
         * @memberof Tooltip
         */


        /**
         * Toggles an element’s tooltip. This is considered a “manual” triggering of the tooltip.
         * @method Tooltip#toggle
         * @memberof Tooltip
         */


        /**
         * Updates the tooltip's title content
         * @method Tooltip#updateTitleContent
         * @memberof Tooltip
         * @param {String|HTMLElement} title - The new content to use for the title
         */


        //
        // Private methods
        //

        createClass(Tooltip, [{
            key: '_create',


            /**
             * Creates a new tooltip node
             * @memberof Tooltip
             * @private
             * @param {HTMLElement} reference
             * @param {String} template
             * @param {String|HTMLElement|TitleFunction} title
             * @param {Boolean} allowHtml
             * @return {HTMLElement} tooltipNode
             */
            value: function _create(reference, template, title, allowHtml) {
                // create tooltip element
                var tooltipGenerator = window.document.createElement('div');
                tooltipGenerator.innerHTML = template.trim();
                var tooltipNode = tooltipGenerator.childNodes[0];

                // add unique ID to our tooltip (needed for accessibility reasons)
                tooltipNode.id = 'tooltip_' + Math.random().toString(36).substr(2, 10);

                // set initial `aria-hidden` state to `false` (it's visible!)
                tooltipNode.setAttribute('aria-hidden', 'false');

                // add title to tooltip
                var titleNode = tooltipGenerator.querySelector(this.options.innerSelector);
                this._addTitleContent(reference, title, allowHtml, titleNode);

                // return the generated tooltip node
                return tooltipNode;
            }
        }, {
            key: '_addTitleContent',
            value: function _addTitleContent(reference, title, allowHtml, titleNode) {
                if (title.nodeType === 1 || title.nodeType === 11) {
                    // if title is a element node or document fragment, append it only if allowHtml is true
                    allowHtml && titleNode.appendChild(title);
                } else if (isFunction(title)) {
                    // if title is a function, call it and set textContent or innerHtml depending by `allowHtml` value
                    var titleText = title.call(reference);
                    allowHtml ? titleNode.innerHTML = titleText : titleNode.textContent = titleText;
                } else {
                    // if it's just a simple text, set textContent or innerHtml depending by `allowHtml` value
                    allowHtml ? titleNode.innerHTML = title : titleNode.textContent = title;
                }
            }
        }, {
            key: '_show',
            value: function _show(reference, options) {
                // don't show if it's already visible
                // or if it's not being showed
                if (this._isOpen && !this._isOpening) {
                    return this;
                }
                this._isOpen = true;

                // if the tooltipNode already exists, just show it
                if (this._tooltipNode) {
                    this._tooltipNode.style.visibility = 'visible';
                    this._tooltipNode.setAttribute('aria-hidden', 'false');
                    this.popperInstance.update();
                    return this;
                }

                // get title
                var title = reference.getAttribute('title') || options.title;

                // don't show tooltip if no title is defined
                if (!title) {
                    return this;
                }

                // create tooltip node
                var tooltipNode = this._create(reference, options.template, title, options.html);

                // Add `aria-describedby` to our reference element for accessibility reasons
                reference.setAttribute('aria-describedby', tooltipNode.id);

                // append tooltip to container
                var container = this._findContainer(options.container, reference);

                this._append(tooltipNode, container);

                this._popperOptions = _extends({}, options.popperOptions, {
                    placement: options.placement
                });

                this._popperOptions.modifiers = _extends({}, this._popperOptions.modifiers, {
                    arrow: {
                        element: this.options.arrowSelector
                    },
                    offset: {
                        offset: options.offset
                    }
                });

                if (options.boundariesElement) {
                    this._popperOptions.modifiers.preventOverflow = {
                        boundariesElement: options.boundariesElement
                    };
                }

                this.popperInstance = new Popper(reference, tooltipNode, this._popperOptions);

                this._tooltipNode = tooltipNode;

                return this;
            }
        }, {
            key: '_hide',
            value: function _hide() /*reference, options*/ {
                // don't hide if it's already hidden
                if (!this._isOpen) {
                    return this;
                }

                this._isOpen = false;

                // hide tooltipNode
                this._tooltipNode.style.visibility = 'hidden';
                this._tooltipNode.setAttribute('aria-hidden', 'true');

                return this;
            }
        }, {
            key: '_dispose',
            value: function _dispose() {
                var _this = this;

                // remove event listeners first to prevent any unexpected behaviour
                this._events.forEach(function (_ref) {
                    var func = _ref.func,
                        event = _ref.event;

                    _this.reference.removeEventListener(event, func);
                });
                this._events = [];

                if (this._tooltipNode) {
                    this._hide();

                    // destroy instance
                    this.popperInstance.destroy();

                    // destroy tooltipNode if removeOnDestroy is not set, as popperInstance.destroy() already removes the element
                    if (!this.popperInstance.options.removeOnDestroy) {
                        this._tooltipNode.parentNode.removeChild(this._tooltipNode);
                        this._tooltipNode = null;
                    }
                }
                return this;
            }
        }, {
            key: '_findContainer',
            value: function _findContainer(container, reference) {
                // if container is a query, get the relative element
                if (typeof container === 'string') {
                    container = window.document.querySelector(container);
                } else if (container === false) {
                    // if container is `false`, set it to reference parent
                    container = reference.parentNode;
                }
                return container;
            }

            /**
             * Append tooltip to container
             * @memberof Tooltip
             * @private
             * @param {HTMLElement} tooltipNode
             * @param {HTMLElement|String|false} container
             */

        }, {
            key: '_append',
            value: function _append(tooltipNode, container) {
                container.appendChild(tooltipNode);
            }
        }, {
            key: '_setEventListeners',
            value: function _setEventListeners(reference, events, options) {
                var _this2 = this;

                var directEvents = [];
                var oppositeEvents = [];

                events.forEach(function (event) {
                    switch (event) {
                        case 'hover':
                            directEvents.push('mouseenter');
                            oppositeEvents.push('mouseleave');
                            break;
                        case 'focus':
                            directEvents.push('focus');
                            oppositeEvents.push('blur');
                            break;
                        case 'click':
                            directEvents.push('click');
                            oppositeEvents.push('click');
                            break;
                    }
                });

                // schedule show tooltip
                directEvents.forEach(function (event) {
                    var func = function func(evt) {
                        if (_this2._isOpening === true) {
                            return;
                        }
                        evt.usedByTooltip = true;
                        _this2._scheduleShow(reference, options.delay, options, evt);
                    };
                    _this2._events.push({event: event, func: func});
                    reference.addEventListener(event, func);
                });

                // schedule hide tooltip
                oppositeEvents.forEach(function (event) {
                    var func = function func(evt) {
                        if (evt.usedByTooltip === true) {
                            return;
                        }
                        _this2._scheduleHide(reference, options.delay, options, evt);
                    };
                    _this2._events.push({event: event, func: func});
                    reference.addEventListener(event, func);
                    if (event === 'click' && options.closeOnClickOutside) {
                        document.addEventListener('mousedown', function (e) {
                            if (!_this2._isOpening) {
                                return;
                            }
                            var popper = _this2.popperInstance.popper;
                            if (reference.contains(e.target) || popper.contains(e.target)) {
                                return;
                            }
                            func(e);
                        }, true);
                    }
                });
            }
        }, {
            key: '_scheduleShow',
            value: function _scheduleShow(reference, delay, options /*, evt */) {
                var _this3 = this;

                this._isOpening = true;
                // defaults to 0
                var computedDelay = delay && delay.show || delay || 0;
                this._showTimeout = window.setTimeout(function () {
                    return _this3._show(reference, options);
                }, computedDelay);
            }
        }, {
            key: '_scheduleHide',
            value: function _scheduleHide(reference, delay, options, evt) {
                var _this4 = this;

                this._isOpening = false;
                // defaults to 0
                var computedDelay = delay && delay.hide || delay || 0;
                window.setTimeout(function () {
                    window.clearTimeout(_this4._showTimeout);
                    if (_this4._isOpen === false) {
                        return;
                    }
                    if (!document.body.contains(_this4._tooltipNode)) {
                        return;
                    }

                    // if we are hiding because of a mouseleave, we must check that the new
                    // reference isn't the tooltip, because in this case we don't want to hide it
                    if (evt.type === 'mouseleave') {
                        var isSet = _this4._setTooltipNodeEvent(evt, reference, delay, options);

                        // if we set the new event, don't hide the tooltip yet
                        // the new event will take care to hide it if necessary
                        if (isSet) {
                            return;
                        }
                    }

                    _this4._hide(reference, options);
                }, computedDelay);
            }
        }, {
            key: '_updateTitleContent',
            value: function _updateTitleContent(title) {
                if (typeof this._tooltipNode === 'undefined') {
                    if (typeof this.options.title !== 'undefined') {
                        this.options.title = title;
                    }
                    return;
                }
                var titleNode = this._tooltipNode.parentNode.querySelector(this.options.innerSelector);
                this._clearTitleContent(titleNode, this.options.html, this.reference.getAttribute('title') || this.options.title);
                this._addTitleContent(this.reference, title, this.options.html, titleNode);
                this.options.title = title;
                this.popperInstance.update();
            }
        }, {
            key: '_clearTitleContent',
            value: function _clearTitleContent(titleNode, allowHtml, lastTitle) {
                if (lastTitle.nodeType === 1 || lastTitle.nodeType === 11) {
                    allowHtml && titleNode.removeChild(lastTitle);
                } else {
                    allowHtml ? titleNode.innerHTML = '' : titleNode.textContent = '';
                }
            }
        }]);
        return Tooltip;
    }();

    /**
     * Title function, its context is the Tooltip instance.
     * @memberof Tooltip
     * @callback TitleFunction
     * @return {String} placement - The desired title.
     */


    var _initialiseProps = function _initialiseProps() {
        var _this5 = this;

        this.show = function () {
            return _this5._show(_this5.reference, _this5.options);
        };

        this.hide = function () {
            return _this5._hide();
        };

        this.dispose = function () {
            return _this5._dispose();
        };

        this.toggle = function () {
            if (_this5._isOpen) {
                return _this5.hide();
            } else {
                return _this5.show();
            }
        };

        this.updateTitleContent = function (title) {
            return _this5._updateTitleContent(title);
        };

        this._events = [];

        this._setTooltipNodeEvent = function (evt, reference, delay, options) {
            var relatedreference = evt.relatedreference || evt.toElement || evt.relatedTarget;

            var callback = function callback(evt2) {
                var relatedreference2 = evt2.relatedreference || evt2.toElement || evt2.relatedTarget;

                // Remove event listener after call
                _this5._tooltipNode.removeEventListener(evt.type, callback);

                // If the new reference is not the reference element
                if (!reference.contains(relatedreference2)) {
                    // Schedule to hide tooltip
                    _this5._scheduleHide(reference, options.delay, options, evt2);
                }
            };

            if (_this5._tooltipNode.contains(relatedreference)) {
                // listen to mouseleave on the tooltip element to be able to hide the tooltip
                _this5._tooltipNode.addEventListener(evt.type, callback);
                return true;
            }

            return false;
        };
    };

    return Tooltip;

})));
//# sourceMappingURL=tooltip.js.map