// https://github.com/HubSpot/drop /*! tether-drop 1.4.1 */ (function(root, factory) { if (typeof define === 'function' && define.amd) { define(["tether"], factory); } else if (typeof exports === 'object') { module.exports = factory(require('tether')); } else { root.Drop = factory(root.Tether); } }(this, function(Tether) { /* global Tether */ 'use strict'; var _bind = Function.prototype.bind; var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); 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 _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _Tether$Utils = Tether.Utils; var extend = _Tether$Utils.extend; var addClass = _Tether$Utils.addClass; var removeClass = _Tether$Utils.removeClass; var hasClass = _Tether$Utils.hasClass; var Evented = _Tether$Utils.Evented; function sortAttach(str) { var _str$split = str.split(' '); var _str$split2 = _slicedToArray(_str$split, 2); var first = _str$split2[0]; var second = _str$split2[1]; if (['left', 'right'].indexOf(first) >= 0) { var _ref = [second, first]; first = _ref[0]; second = _ref[1]; } return [first, second].join(' '); } function removeFromArray(arr, item) { var index = undefined; var results = []; while ((index = arr.indexOf(item)) !== -1) { results.push(arr.splice(index, 1)); } return results; } var clickEvents = ['click']; if ('ontouchstart' in document.documentElement) { clickEvents.push('touchstart'); } var transitionEndEvents = { 'WebkitTransition': 'webkitTransitionEnd', 'MozTransition': 'transitionend', 'OTransition': 'otransitionend', 'transition': 'transitionend' }; var transitionEndEvent = ''; for (var _name in transitionEndEvents) { if (({}).hasOwnProperty.call(transitionEndEvents, _name)) { var tempEl = document.createElement('p'); if (typeof tempEl.style[_name] !== 'undefined') { transitionEndEvent = transitionEndEvents[_name]; } } } var MIRROR_ATTACH = { left: 'right', right: 'left', top: 'bottom', bottom: 'top', middle: 'middle', center: 'center' }; var allDrops = {}; // Drop can be included in external libraries. Calling createContext gives you a fresh // copy of drop which won't interact with other copies on the page (beyond calling the document events). function createContext() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; var drop = function drop() { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return new (_bind.apply(DropInstance, [null].concat(args)))(); }; extend(drop, { createContext: createContext, drops: [], defaults: {} }); var defaultOptions = { classPrefix: 'drop', defaults: { position: 'bottom left', openOn: 'click', beforeClose: null, constrainToScrollParent: true, constrainToWindow: true, classes: '', remove: false, openDelay: 0, closeDelay: 50, // inherited from openDelay and closeDelay if not explicitly defined focusDelay: null, blurDelay: null, hoverOpenDelay: null, hoverCloseDelay: null, tetherOptions: {} } }; extend(drop, defaultOptions, options); extend(drop.defaults, defaultOptions.defaults, options.defaults); if (typeof allDrops[drop.classPrefix] === 'undefined') { allDrops[drop.classPrefix] = []; } drop.updateBodyClasses = function () { // There is only one body, so despite the context concept, we still iterate through all // drops which share our classPrefix. var anyOpen = false; var drops = allDrops[drop.classPrefix]; var len = drops.length; for (var i = 0; i < len; ++i) { if (drops[i].isOpened()) { anyOpen = true; break; } } if (anyOpen) { addClass(document.body, drop.classPrefix + '-open'); } else { removeClass(document.body, drop.classPrefix + '-open'); } }; var DropInstance = (function (_Evented) { _inherits(DropInstance, _Evented); function DropInstance(opts) { _classCallCheck(this, DropInstance); _get(Object.getPrototypeOf(DropInstance.prototype), 'constructor', this).call(this); this.options = extend({}, drop.defaults, opts); this.target = this.options.target; if (typeof this.target === 'undefined') { throw new Error('Drop Error: You must provide a target.'); } var dataPrefix = 'data-' + drop.classPrefix; var contentAttr = this.target.getAttribute(dataPrefix); if (contentAttr && this.options.content == null) { this.options.content = contentAttr; } var attrsOverride = ['position', 'openOn']; for (var i = 0; i < attrsOverride.length; ++i) { var override = this.target.getAttribute(dataPrefix + '-' + attrsOverride[i]); if (override && this.options[attrsOverride[i]] == null) { this.options[attrsOverride[i]] = override; } } if (this.options.classes && this.options.addTargetClasses !== false) { addClass(this.target, this.options.classes); } drop.drops.push(this); allDrops[drop.classPrefix].push(this); this._boundEvents = []; this.bindMethods(); this.setupElements(); this.setupEvents(); this.setupTether(); } _createClass(DropInstance, [{ key: '_on', value: function _on(element, event, handler) { this._boundEvents.push({ element: element, event: event, handler: handler }); element.addEventListener(event, handler); } }, { key: 'bindMethods', value: function bindMethods() { this.transitionEndHandler = this._transitionEndHandler.bind(this); } }, { key: 'setupElements', value: function setupElements() { var _this = this; this.drop = document.createElement('div'); addClass(this.drop, drop.classPrefix); if (this.options.classes) { addClass(this.drop, this.options.classes); } this.content = document.createElement('div'); addClass(this.content, drop.classPrefix + '-content'); if (typeof this.options.content === 'function') { var generateAndSetContent = function generateAndSetContent() { // content function might return a string or an element var contentElementOrHTML = _this.options.content.call(_this, _this); if (typeof contentElementOrHTML === 'string') { _this.content.innerHTML = contentElementOrHTML; } else if (typeof contentElementOrHTML === 'object') { _this.content.innerHTML = ''; _this.content.appendChild(contentElementOrHTML); } else { throw new Error('Drop Error: Content function should return a string or HTMLElement.'); } }; generateAndSetContent(); this.on('open', generateAndSetContent.bind(this)); } else if (typeof this.options.content === 'object') { this.content.appendChild(this.options.content); } else { this.content.innerHTML = this.options.content; } this.drop.appendChild(this.content); } }, { key: 'setupTether', value: function setupTether() { // Tether expects two attachment points, one in the target element, one in the // drop. We use a single one, and use the order as well, to allow us to put // the drop on either side of any of the four corners. This magic converts between // the two: var dropAttach = this.options.position.split(' '); dropAttach[0] = MIRROR_ATTACH[dropAttach[0]]; dropAttach = dropAttach.join(' '); var constraints = []; if (this.options.constrainToScrollParent) { constraints.push({ to: 'scrollParent', pin: 'top, bottom', attachment: 'together none' }); } else { // To get 'out of bounds' classes constraints.push({ to: 'scrollParent' }); } if (this.options.constrainToWindow !== false) { constraints.push({ to: 'window', attachment: 'together' }); } else { // To get 'out of bounds' classes constraints.push({ to: 'window' }); } var opts = { element: this.drop, target: this.target, attachment: sortAttach(dropAttach), targetAttachment: sortAttach(this.options.position), classPrefix: drop.classPrefix, offset: '0 0', targetOffset: '0 0', enabled: false, constraints: constraints, addTargetClasses: this.options.addTargetClasses }; if (this.options.tetherOptions !== false) { this.tether = new Tether(extend({}, opts, this.options.tetherOptions)); } } }, { key: 'setupEvents', value: function setupEvents() { var _this2 = this; if (!this.options.openOn) { return; } if (this.options.openOn === 'always') { setTimeout(this.open.bind(this)); return; } var events = this.options.openOn.split(' '); if (events.indexOf('click') >= 0) { var openHandler = function openHandler(event) { _this2.toggle(event); event.preventDefault(); }; var closeHandler = function closeHandler(event) { if (!_this2.isOpened()) { return; } // Clicking inside dropdown if (event.target === _this2.drop || _this2.drop.contains(event.target)) { return; } // Clicking target if (event.target === _this2.target || _this2.target.contains(event.target)) { return; } _this2.close(event); }; for (var i = 0; i < clickEvents.length; ++i) { var clickEvent = clickEvents[i]; this._on(this.target, clickEvent, openHandler); this._on(document, clickEvent, closeHandler); } } var inTimeout = null; var outTimeout = null; var inHandler = function inHandler(event) { if (outTimeout !== null) { clearTimeout(outTimeout); } else { inTimeout = setTimeout(function () { _this2.open(event); inTimeout = null; }, (event.type === 'focus' ? _this2.options.focusDelay : _this2.options.hoverOpenDelay) || _this2.options.openDelay); } }; var outHandler = function outHandler(event) { if (inTimeout !== null) { clearTimeout(inTimeout); } else { outTimeout = setTimeout(function () { _this2.close(event); outTimeout = null; }, (event.type === 'blur' ? _this2.options.blurDelay : _this2.options.hoverCloseDelay) || _this2.options.closeDelay); } }; if (events.indexOf('hover') >= 0) { this._on(this.target, 'mouseover', inHandler); this._on(this.drop, 'mouseover', inHandler); this._on(this.target, 'mouseout', outHandler); this._on(this.drop, 'mouseout', outHandler); } if (events.indexOf('focus') >= 0) { this._on(this.target, 'focus', inHandler); this._on(this.drop, 'focus', inHandler); this._on(this.target, 'blur', outHandler); this._on(this.drop, 'blur', outHandler); } } }, { key: 'isOpened', value: function isOpened() { if (this.drop) { return hasClass(this.drop, drop.classPrefix + '-open'); } } }, { key: 'toggle', value: function toggle(event) { if (this.isOpened()) { this.close(event); } else { this.open(event); } } }, { key: 'open', value: function open(event) { var _this3 = this; /* eslint no-unused-vars: 0 */ if (this.isOpened()) { return; } if (!this.drop.parentNode) { document.body.appendChild(this.drop); } if (typeof this.tether !== 'undefined') { this.tether.enable(); } addClass(this.drop, drop.classPrefix + '-open'); addClass(this.drop, drop.classPrefix + '-open-transitionend'); setTimeout(function () { if (_this3.drop) { addClass(_this3.drop, drop.classPrefix + '-after-open'); } }); if (typeof this.tether !== 'undefined') { this.tether.position(); } this.trigger('open'); drop.updateBodyClasses(); } }, { key: '_transitionEndHandler', value: function _transitionEndHandler(e) { if (e.target !== e.currentTarget) { return; } if (!hasClass(this.drop, drop.classPrefix + '-open')) { removeClass(this.drop, drop.classPrefix + '-open-transitionend'); } this.drop.removeEventListener(transitionEndEvent, this.transitionEndHandler); } }, { key: 'beforeCloseHandler', value: function beforeCloseHandler(event) { var shouldClose = true; if (!this.isClosing && typeof this.options.beforeClose === 'function') { this.isClosing = true; shouldClose = this.options.beforeClose(event, this) !== false; } this.isClosing = false; return shouldClose; } }, { key: 'close', value: function close(event) { if (!this.isOpened()) { return; } if (!this.beforeCloseHandler(event)) { return; } removeClass(this.drop, drop.classPrefix + '-open'); removeClass(this.drop, drop.classPrefix + '-after-open'); this.drop.addEventListener(transitionEndEvent, this.transitionEndHandler); this.trigger('close'); if (typeof this.tether !== 'undefined') { this.tether.disable(); } drop.updateBodyClasses(); if (this.options.remove) { this.remove(event); } } }, { key: 'remove', value: function remove(event) { this.close(event); if (this.drop.parentNode) { this.drop.parentNode.removeChild(this.drop); } } }, { key: 'position', value: function position() { if (this.isOpened() && typeof this.tether !== 'undefined') { this.tether.position(); } } }, { key: 'destroy', value: function destroy() { this.remove(); if (typeof this.tether !== 'undefined') { this.tether.destroy(); } for (var i = 0; i < this._boundEvents.length; ++i) { var _boundEvents$i = this._boundEvents[i]; var element = _boundEvents$i.element; var _event = _boundEvents$i.event; var handler = _boundEvents$i.handler; element.removeEventListener(_event, handler); } this._boundEvents = []; this.tether = null; this.drop = null; this.content = null; this.target = null; removeFromArray(allDrops[drop.classPrefix], this); removeFromArray(drop.drops, this); } }]); return DropInstance; })(Evented); return drop; } var Drop = createContext(); document.addEventListener('DOMContentLoaded', function () { Drop.updateBodyClasses(); }); return Drop; }));