function Popper(reference, popper, options) {
  this._reference = reference.jquery ? reference[0] : reference
  this.state = {}

  // if the popper variable is a configuration object, parse it to generate an HTMLElement
  // generate a default popper if is not defined
  var isNotDefined = typeof popper === 'undefined' || popper === null
  var isConfig =
    popper && Object.prototype.toString.call(popper) === '[object Object]'
  if (isNotDefined || isConfig) {
    this._popper = this.parse(isConfig ? popper : {})
  }
  // otherwise, use the given HTMLElement as popper
  else {
    this._popper = popper.jquery ? popper[0] : popper
  }

  // with {} we create a new object with the options inside it
  this._options = Object.assign({}, DEFAULTS, options)

  // refactoring modifiers' list
  this._options.modifiers = this._options.modifiers.map(
    function (modifier) {
      // remove ignored modifiers
      if (this._options.modifiersIgnored.indexOf(modifier) !== -1) return

      // set the x-placement attribute before everything else because it could be used to add margins to the popper
      // margins needs to be calculated to get the correct popper offsets
      if (modifier === 'applyStyle') {
        this._popper.setAttribute('x-placement', this._options.placement)
      }

      // return predefined modifier identified by string or keep the custom one
      return this.modifiers[modifier] || modifier
    }.bind(this)
  )

  // make sure to apply the popper position before any computation
  this.state.position = this._getPosition(this._popper, this._reference)
  setStyle(this._popper, { position: this.state.position, top: 0 })

  // fire the first update to position the popper in the right place
  this.update()

  // setup event listeners, they will take care of update the position in specific situations
  this._setupEventListeners()
  return this
}
Create a new Popper.js instance
@constructor Popper
@param {HTMLElement} reference - The reference element used to position the popper
@param {HTMLElement|Object} popper
     The HTML element used as popper, or a configuration used to generate the popper.
@param {String} [popper.tagName='div'] The tag name of the generated popper.
@param {Array} [popper.classNames=['popper']] Array of classes to apply to the generated popper.
@param {Array} [popper.attributes] Array of attributes to apply, specify `attr:value` to assign a value to it.
@param {HTMLElement|String} [popper.parent=window.document.body] The parent element, given as HTMLElement or as query string.
@param {String} [popper.content=''] The content of the popper, it can be text, html, or node; if it is not text, set `contentType` to `html` or `node`.
@param {String} [popper.contentType='text'] If `html`, the `content` will be parsed as HTML. If `node`, it will be appended as-is.
@param {String} [popper.arrowTagName='div'] Same as `popper.tagName` but for the arrow element.
@param {Array} [popper.arrowClassNames='popper__arrow'] Same as `popper.classNames` but for the arrow element.
@param {String} [popper.arrowAttributes=['x-arrow']] Same as `popper.attributes` but for the arrow element.
@param {Object} options
@param {String} [options.placement=bottom]
     Placement of the popper accepted values: `top(-start, -end), right(-start, -end), bottom(-start, -right),
     left(-start, -end)`

@param {HTMLElement|String} [options.arrowElement='[x-arrow]']
     The DOM Node used as arrow for the popper, or a CSS selector used to get the DOM node. It must be child of
     its parent Popper. Popper.js will apply to the given element the style required to align the arrow with its
     reference element.
     By default, it will look for a child node of the popper with the `x-arrow` attribute.

@param {Boolean} [options.gpuAcceleration=true]
     When this property is set to true, the popper position will be applied using CSS3 translate3d, allowing the
     browser to use the GPU to accelerate the rendering.
     If set to false, the popper will be placed using `top` and `left` properties, not using the GPU.

@param {Number} [options.offset=0]
     Amount of pixels the popper will be shifted (can be negative).

@param {String|Element} [options.boundariesElement='viewport']
     The element which will define the boundaries of the popper position, the popper will never be placed outside
     of the defined boundaries (except if `keepTogether` is enabled)

@param {Number} [options.boundariesPadding=5]
     Additional padding for the boundaries

@param {Array} [options.preventOverflowOrder=['left', 'right', 'top', 'bottom']]
     Order used when Popper.js tries to avoid overflows from the boundaries, they will be checked in order,
     this means that the last ones will never overflow

@param {String|Array} [options.flipBehavior='flip']
     The behavior used by the `flip` modifier to change the placement of the popper when the latter is trying to
     overlap its reference element. Defining `flip` as value, the placement will be flipped on
     its axis (`right - left`, `top - bottom`).
     You can even pass an array of placements (eg: `['right', 'left', 'top']` ) to manually specify
     how alter the placement when a flip is needed. (eg. in the above example, it would first flip from right to left,
     then, if even in its new placement, the popper is overlapping its reference element, it will be moved to top)

@param {Array} [options.modifiers=[ 'shift', 'offset', 'preventOverflow', 'keepTogether', 'arrow', 'flip', 'applyStyle']]
     List of functions used to modify the data before they are applied to the popper, add your custom functions
     to this array to edit the offsets and placement.
     The function should reflect the @params and @returns of preventOverflow

@param {Array} [options.modifiersIgnored=[]]
     Put here any built-in modifier name you want to exclude from the modifiers list
     The function should reflect the @params and @returns of preventOverflow

@param {Boolean} [options.removeOnDestroy=false]
     Set to true if you want to automatically remove the popper when you call the `destroy` method.