bootstrap.js 97.5 KB
Newer Older
Mark Otto's avatar
Mark Otto committed
3001
3002
3003
3004
 * @param {Object=} opt_config
 * @constructor
 */
var Tooltip = function (element, opt_config) {
XhmikosR's avatar
XhmikosR committed
3005

Mark Otto's avatar
Mark Otto committed
3006
3007
  /** @private {boolean} */
  this._isEnabled = true
XhmikosR's avatar
XhmikosR committed
3008

Mark Otto's avatar
Mark Otto committed
3009
3010
  /** @private {number} */
  this._timeout = 0
XhmikosR's avatar
XhmikosR committed
3011

Mark Otto's avatar
Mark Otto committed
3012
3013
  /** @private {string} */
  this._hoverState = ''
XhmikosR's avatar
XhmikosR committed
3014

Mark Otto's avatar
Mark Otto committed
3015
3016
  /** @protected {Element} */
  this.element = element
XhmikosR's avatar
XhmikosR committed
3017

Mark Otto's avatar
Mark Otto committed
3018
3019
  /** @protected {Object} */
  this.config = this._getConfig(opt_config)
XhmikosR's avatar
XhmikosR committed
3020

Mark Otto's avatar
Mark Otto committed
3021
3022
  /** @protected {Element} */
  this.tip = null
XhmikosR's avatar
XhmikosR committed
3023

Mark Otto's avatar
Mark Otto committed
3024
3025
  /** @protected {Element} */
  this.arrow = null
XhmikosR's avatar
XhmikosR committed
3026

Mark Otto's avatar
Mark Otto committed
3027
3028
3029
3030
  if (this.config['viewport']) {

    /** @private {Element} */
    this._viewport = $(this.config['viewport']['selector'] || this.config['viewport'])[0]
XhmikosR's avatar
XhmikosR committed
3031
3032
3033

  }

Mark Otto's avatar
Mark Otto committed
3034
3035
  this._setListeners()
}
Heinrich Fenkart's avatar
grunt    
Heinrich Fenkart committed
3036
3037


Mark Otto's avatar
Mark Otto committed
3038
3039
3040
3041
3042
/**
 * @const
 * @type {string}
 */
Tooltip['VERSION']  = '4.0.0'
Heinrich Fenkart's avatar
grunt    
Heinrich Fenkart committed
3043

Heinrich Fenkart's avatar
grunt    
Heinrich Fenkart committed
3044

Mark Otto's avatar
Mark Otto committed
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
/**
 * @const
 * @type {Object}
 */
Tooltip['Defaults'] = {
  'container' : false,
  'animation' : true,
  'placement' : 'top',
  'selector'  : false,
  'template'  : '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
  'trigger'   : 'hover focus',
  'title'     : '',
  'delay'     : 0,
  'html'      : false,
  'viewport': {
    'selector': 'body',
    'padding' : 0
Heinrich Fenkart's avatar
grunt    
Heinrich Fenkart committed
3062
  }
Mark Otto's avatar
Mark Otto committed
3063
}
Heinrich Fenkart's avatar
grunt    
Heinrich Fenkart committed
3064
3065


Mark Otto's avatar
Mark Otto committed
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
/**
 * @const
 * @enum {string}
 * @protected
 */
Tooltip.Direction = {
  TOP: 'top',
  LEFT: 'left',
  RIGHT: 'right',
  BOTTOM: 'bottom'
}
XhmikosR's avatar
XhmikosR committed
3077
3078


Mark Otto's avatar
Mark Otto committed
3079
3080
3081
3082
3083
3084
/**
 * @const
 * @type {string}
 * @private
 */
Tooltip._NAME = 'tooltip'
XhmikosR's avatar
XhmikosR committed
3085
3086


Mark Otto's avatar
Mark Otto committed
3087
3088
3089
3090
3091
3092
/**
 * @const
 * @type {string}
 * @private
 */
Tooltip._DATA_KEY = 'bs.tooltip'
XhmikosR's avatar
XhmikosR committed
3093
3094


Mark Otto's avatar
Mark Otto committed
3095
3096
3097
3098
3099
3100
/**
 * @const
 * @type {number}
 * @private
 */
Tooltip._TRANSITION_DURATION = 150
XhmikosR's avatar
XhmikosR committed
3101
3102


Mark Otto's avatar
Mark Otto committed
3103
3104
3105
3106
3107
3108
3109
3110
3111
/**
 * @const
 * @enum {string}
 * @private
 */
Tooltip._HoverState = {
  IN: 'in',
  OUT: 'out'
}
XhmikosR's avatar
XhmikosR committed
3112
3113


Mark Otto's avatar
Mark Otto committed
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
/**
 * @const
 * @enum {string}
 * @private
 */
Tooltip._Event = {
  HIDE   : 'hide.bs.tooltip',
  HIDDEN : 'hidden.bs.tooltip',
  SHOW   : 'show.bs.tooltip',
  SHOWN  : 'shown.bs.tooltip'
}
XhmikosR's avatar
XhmikosR committed
3125
3126


Mark Otto's avatar
Mark Otto committed
3127
3128
3129
3130
3131
3132
3133
3134
3135
/**
 * @const
 * @enum {string}
 * @private
 */
Tooltip._ClassName = {
  FADE : 'fade',
  IN   : 'in'
}
XhmikosR's avatar
XhmikosR committed
3136
3137


Mark Otto's avatar
Mark Otto committed
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
/**
 * @const
 * @enum {string}
 * @private
 */
Tooltip._Selector = {
  TOOLTIP       : '.tooltip',
  TOOLTIP_INNER : '.tooltip-inner',
  TOOLTIP_ARROW : '.tooltip-arrow'
}
XhmikosR's avatar
XhmikosR committed
3148
3149


Mark Otto's avatar
Mark Otto committed
3150
3151
3152
3153
3154
3155
/**
 * @const
 * @type {Function}
 * @private
 */
Tooltip._JQUERY_NO_CONFLICT = $.fn[Tooltip._NAME]
XhmikosR's avatar
XhmikosR committed
3156
3157


Mark Otto's avatar
Mark Otto committed
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
/**
 * @param {Object=} opt_config
 * @this {jQuery}
 * @return {jQuery}
 * @private
 */
Tooltip._jQueryInterface = function (opt_config) {
  return this.each(function () {
    var data   = $(this).data(Tooltip._DATA_KEY)
    var config = typeof opt_config == 'object' ? opt_config : null
XhmikosR's avatar
XhmikosR committed
3168

Mark Otto's avatar
Mark Otto committed
3169
3170
3171
    if (!data && opt_config == 'destroy') {
      return
    }
XhmikosR's avatar
XhmikosR committed
3172

Mark Otto's avatar
Mark Otto committed
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
    if (!data) {
      data = new Tooltip(this, config)
      $(this).data(Tooltip._DATA_KEY, data)
    }

    if (typeof opt_config === 'string') {
      data[opt_config]()
    }
  })
}
XhmikosR's avatar
XhmikosR committed
3183
3184


Mark Otto's avatar
Mark Otto committed
3185
3186
3187
3188
3189
3190
/**
 * Enable tooltip
 */
Tooltip.prototype['enable'] = function () {
  this._isEnabled = true
}
XhmikosR's avatar
XhmikosR committed
3191
3192


Mark Otto's avatar
Mark Otto committed
3193
3194
3195
3196
3197
3198
/**
 * Disable tooltip
 */
Tooltip.prototype['disable'] = function () {
  this._isEnabled = false
}
XhmikosR's avatar
XhmikosR committed
3199
3200


Mark Otto's avatar
Mark Otto committed
3201
3202
3203
3204
3205
3206
/**
 * Toggle the tooltip enable state
 */
Tooltip.prototype['toggleEnabled'] = function () {
  this._isEnabled = !this._isEnabled
}
3207

Mark Otto's avatar
Mark Otto committed
3208
3209
3210
3211
3212
3213
3214
/**
 * Toggle the tooltips display
 * @param {Event} opt_event
 */
Tooltip.prototype['toggle'] = function (opt_event) {
  var context = this
  var dataKey = this.getDataKey()
XhmikosR's avatar
XhmikosR committed
3215

Mark Otto's avatar
Mark Otto committed
3216
3217
  if (opt_event) {
    context = $(opt_event.currentTarget).data(dataKey)
XhmikosR's avatar
XhmikosR committed
3218

Mark Otto's avatar
Mark Otto committed
3219
3220
3221
    if (!context) {
      context = new this.constructor(opt_event.currentTarget, this._getDelegateConfig())
      $(opt_event.currentTarget).data(dataKey, context)
XhmikosR's avatar
XhmikosR committed
3222
    }
Mark Otto's avatar
Mark Otto committed
3223
  }
XhmikosR's avatar
XhmikosR committed
3224

Mark Otto's avatar
Mark Otto committed
3225
3226
3227
3228
  $(context.getTipElement()).hasClass(Tooltip._ClassName.IN) ?
    context._leave(null, context) :
    context._enter(null, context)
}
XhmikosR's avatar
XhmikosR committed
3229
3230


Mark Otto's avatar
Mark Otto committed
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
/**
 * Remove tooltip functionality
 */
Tooltip.prototype['destroy'] = function () {
  clearTimeout(this._timeout)
  this['hide'](function () {
    $(this.element)
      .off(Tooltip._Selector.TOOLTIP)
      .removeData(this.getDataKey())
  }.bind(this))
}
XhmikosR's avatar
XhmikosR committed
3242
3243


Mark Otto's avatar
Mark Otto committed
3244
3245
3246
3247
3248
3249
3250
/**
 * Show the tooltip
 * todo (fat): ~fuck~ this is a big function - refactor out all of positioning logic
 * and replace with external lib
 */
Tooltip.prototype['show'] = function () {
  var showEvent = $.Event(this.getEventObject().SHOW)
XhmikosR's avatar
XhmikosR committed
3251

Mark Otto's avatar
Mark Otto committed
3252
3253
  if (this.isWithContent() && this._isEnabled) {
    $(this.element).trigger(showEvent)
XhmikosR's avatar
XhmikosR committed
3254

Mark Otto's avatar
Mark Otto committed
3255
    var isInTheDom = $.contains(this.element.ownerDocument.documentElement, this.element)
XhmikosR's avatar
XhmikosR committed
3256

Mark Otto's avatar
Mark Otto committed
3257
3258
    if (showEvent.isDefaultPrevented() || !isInTheDom) {
      return
XhmikosR's avatar
XhmikosR committed
3259
3260
    }

Mark Otto's avatar
Mark Otto committed
3261
3262
    var tip   = this.getTipElement()
    var tipId = Bootstrap.getUID(this.getName())
XhmikosR's avatar
XhmikosR committed
3263

Mark Otto's avatar
Mark Otto committed
3264
3265
    tip.setAttribute('id', tipId)
    this.element.setAttribute('aria-describedby', tipId)
XhmikosR's avatar
XhmikosR committed
3266

Mark Otto's avatar
Mark Otto committed
3267
    this.setContent()
XhmikosR's avatar
XhmikosR committed
3268

Mark Otto's avatar
Mark Otto committed
3269
3270
3271
    if (this.config['animation']) {
      $(tip).addClass(Tooltip._ClassName.FADE)
    }
XhmikosR's avatar
XhmikosR committed
3272

Mark Otto's avatar
Mark Otto committed
3273
3274
3275
    var placement = typeof this.config['placement'] == 'function' ?
      this.config['placement'].call(this, tip, this.element) :
      this.config['placement']
XhmikosR's avatar
XhmikosR committed
3276

Mark Otto's avatar
Mark Otto committed
3277
3278
3279
3280
3281
    var autoToken = /\s?auto?\s?/i
    var isWithAutoPlacement = autoToken.test(placement)

    if (isWithAutoPlacement) {
      placement = placement.replace(autoToken, '') || Tooltip.Direction.TOP
XhmikosR's avatar
XhmikosR committed
3282
3283
    }

Mark Otto's avatar
Mark Otto committed
3284
3285
    if (tip.parentNode && tip.parentNode.nodeType == Node.ELEMENT_NODE) {
      tip.parentNode.removeChild(tip)
XhmikosR's avatar
XhmikosR committed
3286
3287
    }

Mark Otto's avatar
Mark Otto committed
3288
3289
3290
    tip.style.top     = 0
    tip.style.left    = 0
    tip.style.display = 'block'
XhmikosR's avatar
XhmikosR committed
3291

Mark Otto's avatar
Mark Otto committed
3292
    $(tip).addClass(Tooltip._NAME + '-' + placement)
XhmikosR's avatar
XhmikosR committed
3293

Mark Otto's avatar
Mark Otto committed
3294
    $(tip).data(this.getDataKey(), this)
XhmikosR's avatar
XhmikosR committed
3295

Mark Otto's avatar
Mark Otto committed
3296
3297
3298
3299
3300
    if (this.config['container']) {
      $(this.config['container'])[0].appendChild(tip)
    } else {
      this.element.parentNode.insertBefore(tip, this.element.nextSibling)
    }
XhmikosR's avatar
XhmikosR committed
3301

Mark Otto's avatar
Mark Otto committed
3302
3303
3304
    var position            = this._getPosition()
    var actualWidth         = tip.offsetWidth
    var actualHeight        = tip.offsetHeight
XhmikosR's avatar
XhmikosR committed
3305

Mark Otto's avatar
Mark Otto committed
3306
3307
    var calculatedPlacement = this._getCalculatedAutoPlacement(isWithAutoPlacement, placement, position, actualWidth, actualHeight)
    var calculatedOffset    = this._getCalculatedOffset(calculatedPlacement, position, actualWidth, actualHeight)
XhmikosR's avatar
XhmikosR committed
3308

Mark Otto's avatar
Mark Otto committed
3309
    this._applyCalculatedPlacement(calculatedOffset, calculatedPlacement)
XhmikosR's avatar
XhmikosR committed
3310

Mark Otto's avatar
Mark Otto committed
3311
3312
3313
3314
    var complete = function () {
      var prevHoverState = this.hoverState
      $(this.element).trigger(this.getEventObject().SHOWN)
      this.hoverState = null
XhmikosR's avatar
XhmikosR committed
3315

Mark Otto's avatar
Mark Otto committed
3316
3317
      if (prevHoverState == 'out') this._leave(null, this)
    }.bind(this)
XhmikosR's avatar
XhmikosR committed
3318

Mark Otto's avatar
Mark Otto committed
3319
3320
3321
3322
3323
    Bootstrap.transition && $(this._tip).hasClass(Tooltip._ClassName.FADE) ?
      $(this._tip)
        .one(Bootstrap.TRANSITION_END, complete)
        .emulateTransitionEnd(Tooltip._TRANSITION_DURATION) :
      complete()
XhmikosR's avatar
XhmikosR committed
3324
  }
Mark Otto's avatar
Mark Otto committed
3325
}
XhmikosR's avatar
XhmikosR committed
3326
3327


Mark Otto's avatar
Mark Otto committed
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
/**
 * Hide the tooltip breh
 */
Tooltip.prototype['hide'] = function (callback) {
  var tip       = this.getTipElement()
  var hideEvent = $.Event(this.getEventObject().HIDE)

  var complete  = function () {
    if (this._hoverState != Tooltip._HoverState.IN) {
      tip.parentNode.removeChild(tip)
    }
XhmikosR's avatar
XhmikosR committed
3339

Mark Otto's avatar
Mark Otto committed
3340
3341
    this.element.removeAttribute('aria-describedby')
    $(this.element).trigger(this.getEventObject().HIDDEN)
XhmikosR's avatar
XhmikosR committed
3342

Mark Otto's avatar
Mark Otto committed
3343
3344
3345
3346
    if (callback) {
      callback()
    }
  }.bind(this)
XhmikosR's avatar
XhmikosR committed
3347

Mark Otto's avatar
Mark Otto committed
3348
  $(this.element).trigger(hideEvent)
XhmikosR's avatar
XhmikosR committed
3349

Mark Otto's avatar
Mark Otto committed
3350
  if (hideEvent.isDefaultPrevented()) return
XhmikosR's avatar
XhmikosR committed
3351

Mark Otto's avatar
Mark Otto committed
3352
  $(tip).removeClass(Tooltip._ClassName.IN)
XhmikosR's avatar
XhmikosR committed
3353

Mark Otto's avatar
Mark Otto committed
3354
3355
3356
3357
3358
3359
3360
  if (Bootstrap.transition && $(this._tip).hasClass(Tooltip._ClassName.FADE)) {
    $(tip)
      .one(Bootstrap.TRANSITION_END, complete)
      .emulateTransitionEnd(Tooltip._TRANSITION_DURATION)
  } else {
    complete()
  }
XhmikosR's avatar
XhmikosR committed
3361

Mark Otto's avatar
Mark Otto committed
3362
3363
  this._hoverState = ''
}
XhmikosR's avatar
XhmikosR committed
3364
3365


Mark Otto's avatar
Mark Otto committed
3366
3367
3368
3369
3370
3371
/**
 * @return {string}
 */
Tooltip.prototype['getHoverState'] = function (callback) {
  return this._hoverState
}
XhmikosR's avatar
XhmikosR committed
3372
3373


Mark Otto's avatar
Mark Otto committed
3374
3375
3376
3377
3378
3379
3380
/**
 * @return {string}
 * @protected
 */
Tooltip.prototype.getName = function () {
  return Tooltip._NAME
}
XhmikosR's avatar
XhmikosR committed
3381
3382


Mark Otto's avatar
Mark Otto committed
3383
3384
3385
3386
3387
3388
3389
/**
 * @return {string}
 * @protected
 */
Tooltip.prototype.getDataKey = function () {
  return Tooltip._DATA_KEY
}
XhmikosR's avatar
XhmikosR committed
3390
3391


Mark Otto's avatar
Mark Otto committed
3392
3393
3394
3395
3396
3397
3398
/**
 * @return {Object}
 * @protected
 */
Tooltip.prototype.getEventObject = function () {
  return Tooltip._Event
}
XhmikosR's avatar
XhmikosR committed
3399

Heinrich Fenkart's avatar
grunt    
Heinrich Fenkart committed
3400

Mark Otto's avatar
Mark Otto committed
3401
3402
3403
3404
3405
3406
/**
 * @return {string}
 * @protected
 */
Tooltip.prototype.getTitle = function () {
  var title = this.element.getAttribute('data-original-title')
XhmikosR's avatar
XhmikosR committed
3407

Mark Otto's avatar
Mark Otto committed
3408
3409
3410
3411
  if (!title) {
    title = typeof this.config['title'] === 'function' ?
      this.config['title'].call(this.element) :
      this.config['title']
XhmikosR's avatar
XhmikosR committed
3412
3413
  }

Mark Otto's avatar
Mark Otto committed
3414
3415
  return /** @type {string} */ (title)
}
XhmikosR's avatar
XhmikosR committed
3416
3417


Mark Otto's avatar
Mark Otto committed
3418
3419
3420
3421
3422
3423
3424
/**
 * @return {Element}
 * @protected
 */
Tooltip.prototype.getTipElement = function () {
  return (this._tip = this._tip || $(this.config['template'])[0])
}
XhmikosR's avatar
XhmikosR committed
3425
3426


Mark Otto's avatar
Mark Otto committed
3427
3428
3429
3430
3431
3432
3433
/**
 * @return {Element}
 * @protected
 */
Tooltip.prototype.getArrowElement = function () {
  return (this.arrow = this.arrow || $(this.getTipElement()).find(Tooltip._Selector.TOOLTIP_ARROW)[0])
}
XhmikosR's avatar
XhmikosR committed
3434
3435


Mark Otto's avatar
Mark Otto committed
3436
3437
3438
3439
3440
3441
3442
/**
 * @return {boolean}
 * @protected
 */
Tooltip.prototype.isWithContent = function () {
  return !!this.getTitle()
}
XhmikosR's avatar
XhmikosR committed
3443
3444


Mark Otto's avatar
Mark Otto committed
3445
3446
3447
3448
3449
3450
/**
 * @protected
 */
Tooltip.prototype.setContent = function () {
  var tip   = this.getTipElement()
  var title = this.getTitle()
XhmikosR's avatar
XhmikosR committed
3451

Mark Otto's avatar
Mark Otto committed
3452
  $(tip).find(Tooltip._Selector.TOOLTIP_INNER)[0][this.config['html'] ? 'innerHTML' : 'innerText'] = title
XhmikosR's avatar
XhmikosR committed
3453

Mark Otto's avatar
Mark Otto committed
3454
3455
3456
  $(tip)
    .removeClass(Tooltip._ClassName.FADE)
    .removeClass(Tooltip._ClassName.IN)
XhmikosR's avatar
XhmikosR committed
3457

Mark Otto's avatar
Mark Otto committed
3458
3459
  for (var direction in Tooltip.Direction) {
    $(tip).removeClass(Tooltip._NAME + '-' + direction)
XhmikosR's avatar
XhmikosR committed
3460
  }
Mark Otto's avatar
Mark Otto committed
3461
}
XhmikosR's avatar
XhmikosR committed
3462
3463


Mark Otto's avatar
Mark Otto committed
3464
3465
3466
3467
3468
/**
 * @private
 */
Tooltip.prototype._setListeners = function () {
  var triggers = this.config['trigger'].split(' ')
XhmikosR's avatar
XhmikosR committed
3469

Mark Otto's avatar
Mark Otto committed
3470
3471
3472
  triggers.forEach(function (trigger) {
    if (trigger == 'click') {
      $(this.element).on('click.bs.tooltip', this.config['selector'], this['toggle'].bind(this))
XhmikosR's avatar
XhmikosR committed
3473

Mark Otto's avatar
Mark Otto committed
3474
3475
3476
    } else if (trigger != 'manual') {
      var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
      var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
XhmikosR's avatar
XhmikosR committed
3477

Mark Otto's avatar
Mark Otto committed
3478
3479
3480
      $(this.element)
        .on(eventIn  + '.bs.tooltip', this.config['selector'], this._enter.bind(this))
        .on(eventOut + '.bs.tooltip', this.config['selector'], this._leave.bind(this))
XhmikosR's avatar
XhmikosR committed
3481
    }
Mark Otto's avatar
Mark Otto committed
3482
  }.bind(this))
XhmikosR's avatar
XhmikosR committed
3483

Mark Otto's avatar
Mark Otto committed
3484
3485
3486
3487
3488
3489
  if (this.config['selector']) {
    this.config = $.extend({}, this.config, { 'trigger': 'manual', 'selector': '' })
  } else {
    this._fixTitle()
  }
}
XhmikosR's avatar
XhmikosR committed
3490
3491


Mark Otto's avatar
Mark Otto committed
3492
3493
3494
3495
3496
3497
3498
/**
 * @param {Object=} opt_config
 * @return {Object}
 * @private
 */
Tooltip.prototype._getConfig = function (opt_config) {
  var config = $.extend({}, this.constructor['Defaults'], $(this.element).data(), opt_config)
XhmikosR's avatar
XhmikosR committed
3499

Mark Otto's avatar
Mark Otto committed
3500
3501
3502
3503
3504
3505
  if (config['delay'] && typeof config['delay'] == 'number') {
    config['delay'] = {
      'show': config['delay'],
      'hide': config['delay']
    }
  }
XhmikosR's avatar
XhmikosR committed
3506

Mark Otto's avatar
Mark Otto committed
3507
3508
  return config
}
XhmikosR's avatar
XhmikosR committed
3509
3510


Mark Otto's avatar
Mark Otto committed
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
/**
 * @return {Object}
 * @private
 */
Tooltip.prototype._getDelegateConfig = function () {
  var config  = {}
  var defaults = this.constructor['Defaults']

  if (this.config) {
    for (var key in this.config) {
      var value = this.config[key]
      if (defaults[key] != value) config[key] = value
XhmikosR's avatar
XhmikosR committed
3523
3524
3525
    }
  }

Mark Otto's avatar
Mark Otto committed
3526
3527
  return config
}
Mark Otto's avatar
grunt    
Mark Otto committed
3528
3529
3530



Mark Otto's avatar
Mark Otto committed
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
/**
 * @param {boolean} isWithAutoPlacement
 * @param {string} placement
 * @param {Object} position
 * @param {number} actualWidth
 * @param {number} actualHeight
 * @return {string}
 * @private
 */
Tooltip.prototype._getCalculatedAutoPlacement = function (isWithAutoPlacement, placement, position, actualWidth, actualHeight) {
  if (isWithAutoPlacement) {
    var originalPlacement = placement
    var container         = this.config['container'] ? $(this.config['container'])[0] : this.element.parentNode
    var containerDim      = this._getPosition(/** @type {Element} */ (container))

    placement = placement == Tooltip.Direction.BOTTOM && position.bottom + actualHeight > containerDim.bottom ? Tooltip.Direction.TOP    :
                placement == Tooltip.Direction.TOP    && position.top    - actualHeight < containerDim.top    ? Tooltip.Direction.BOTTOM :
                placement == Tooltip.Direction.RIGHT  && position.right  + actualWidth  > containerDim.width  ? Tooltip.Direction.LEFT   :
                placement == Tooltip.Direction.LEFT   && position.left   - actualWidth  < containerDim.left   ? Tooltip.Direction.RIGHT  :
                placement

    $(this._tip)
      .removeClass(Tooltip._NAME + '-' + originalPlacement)
      .addClass(Tooltip._NAME + '-' + placement)
XhmikosR's avatar
XhmikosR committed
3555
3556
  }

Mark Otto's avatar
Mark Otto committed
3557
3558
  return placement
}
XhmikosR's avatar
XhmikosR committed
3559
3560


Mark Otto's avatar
Mark Otto committed
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
/**
 * @param {string} placement
 * @param {Object} position
 * @param {number} actualWidth
 * @param {number} actualHeight
 * @return {{left: number, top: number}}
 * @private
 */
Tooltip.prototype._getCalculatedOffset = function (placement, position, actualWidth, actualHeight) {
  return placement == Tooltip.Direction.BOTTOM ? { top: position.top + position.height,   left: position.left + position.width / 2 - actualWidth / 2  } :
         placement == Tooltip.Direction.TOP    ? { top: position.top - actualHeight,      left: position.left + position.width / 2 - actualWidth / 2  } :
         placement == Tooltip.Direction.LEFT   ? { top: position.top + position.height / 2 - actualHeight / 2, left: position.left - actualWidth      } :
      /* placement == Tooltip.Direction.RIGHT */ { top: position.top + position.height / 2 - actualHeight / 2, left: position.left + position.width   }
}
XhmikosR's avatar
XhmikosR committed
3575
3576


Mark Otto's avatar
Mark Otto committed
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
/**
 * @param {string} placement
 * @param {Object} position
 * @param {number} actualWidth
 * @param {number} actualHeight
 * @return {Object}
 * @private
 */
Tooltip.prototype._getViewportAdjustedDelta = function (placement, position, actualWidth, actualHeight) {
  var delta = { top: 0, left: 0 }
XhmikosR's avatar
XhmikosR committed
3587

Mark Otto's avatar
Mark Otto committed
3588
  if (!this._viewport) {
XhmikosR's avatar
XhmikosR committed
3589
3590
3591
    return delta
  }

Mark Otto's avatar
Mark Otto committed
3592
3593
  var viewportPadding    = this.config['viewport'] && this.config['viewport']['padding'] || 0
  var viewportDimensions = this._getPosition(this._viewport)
XhmikosR's avatar
XhmikosR committed
3594

Mark Otto's avatar
Mark Otto committed
3595
3596
3597
  if (placement === Tooltip.Direction.RIGHT || placement === Tooltip.Direction.LEFT) {
    var topEdgeOffset    = position.top - viewportPadding - viewportDimensions.scroll
    var bottomEdgeOffset = position.top + viewportPadding - viewportDimensions.scroll + actualHeight
XhmikosR's avatar
XhmikosR committed
3598

Mark Otto's avatar
Mark Otto committed
3599
3600
    if (topEdgeOffset < viewportDimensions.top) { // top overflow
      delta.top = viewportDimensions.top - topEdgeOffset
XhmikosR's avatar
XhmikosR committed
3601

Mark Otto's avatar
Mark Otto committed
3602
3603
3604
    } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
      delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
    }
XhmikosR's avatar
XhmikosR committed
3605

Mark Otto's avatar
Mark Otto committed
3606
3607
3608
  } else {
    var leftEdgeOffset  = position.left - viewportPadding
    var rightEdgeOffset = position.left + viewportPadding + actualWidth
XhmikosR's avatar
XhmikosR committed
3609

Mark Otto's avatar
Mark Otto committed
3610
3611
    if (leftEdgeOffset < viewportDimensions.left) { // left overflow
      delta.left = viewportDimensions.left - leftEdgeOffset
XhmikosR's avatar
XhmikosR committed
3612

Mark Otto's avatar
Mark Otto committed
3613
3614
3615
    } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
      delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
    }
XhmikosR's avatar
XhmikosR committed
3616
3617
  }

Mark Otto's avatar
Mark Otto committed
3618
3619
  return delta
}
XhmikosR's avatar
XhmikosR committed
3620
3621


Mark Otto's avatar
Mark Otto committed
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
/**
 * @param {Element=} opt_element
 * @return {Object}
 * @private
 */
Tooltip.prototype._getPosition = function (opt_element) {
  var element   = opt_element || this.element
  var isBody    = element.tagName == 'BODY'
  var rect      = element.getBoundingClientRect()
  var offset    = isBody ? { top: 0, left: 0 } : $(element).offset()
  var scroll    = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : this.element.scrollTop }
  var outerDims = isBody ? { width: window.innerWidth, height: window.innerHeight } : null

  return $.extend({}, rect, scroll, outerDims, offset)
}
XhmikosR's avatar
XhmikosR committed
3637
3638


Mark Otto's avatar
Mark Otto committed
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
/**
 * @param {{left: number, top: number}} offset
 * @param {string} placement
 * @private
 */
Tooltip.prototype._applyCalculatedPlacement = function (offset, placement) {
  var tip    = this.getTipElement()
  var width  = tip.offsetWidth
  var height = tip.offsetHeight

  // manually read margins because getBoundingClientRect includes difference
  var marginTop  = parseInt(tip.style.marginTop, 10)
  var marginLeft = parseInt(tip.style.marginLeft, 10)

  // we must check for NaN for ie 8/9
  if (isNaN(marginTop))  {
    marginTop  = 0
XhmikosR's avatar
XhmikosR committed
3656
  }
Mark Otto's avatar
Mark Otto committed
3657
3658
3659
3660
3661
3662
  if (isNaN(marginLeft)) {
    marginLeft = 0
  }

  offset.top  = offset.top  + marginTop
  offset.left = offset.left + marginLeft
XhmikosR's avatar
XhmikosR committed
3663

Mark Otto's avatar
Mark Otto committed
3664
3665
3666
3667
3668
3669
3670
3671
  // $.fn.offset doesn't round pixel values
  // so we use setOffset directly with our own function B-0
  $.offset.setOffset(tip, $.extend({
    using: function (props) {
      tip.style.top  = Math.round(props.top)  + 'px'
      tip.style.left = Math.round(props.left) + 'px'
    }
  }, offset), 0)
XhmikosR's avatar
XhmikosR committed
3672

Mark Otto's avatar
Mark Otto committed
3673
  $(tip).addClass(Tooltip._ClassName.IN)
XhmikosR's avatar
XhmikosR committed
3674

Mark Otto's avatar
Mark Otto committed
3675
3676
3677
  // check to see if placing tip in new offset caused the tip to resize itself
  var actualWidth  = tip.offsetWidth
  var actualHeight = tip.offsetHeight
XhmikosR's avatar
XhmikosR committed
3678

Mark Otto's avatar
Mark Otto committed
3679
3680
  if (placement == Tooltip.Direction.TOP && actualHeight != height) {
    offset.top = offset.top + height - actualHeight
XhmikosR's avatar
XhmikosR committed
3681
3682
  }

Mark Otto's avatar
Mark Otto committed
3683
  var delta = this._getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
XhmikosR's avatar
XhmikosR committed
3684

Mark Otto's avatar
Mark Otto committed
3685
3686
3687
3688
3689
  if (delta.left) {
    offset.left += delta.left
  } else {
    offset.top  += delta.top
  }
XhmikosR's avatar
XhmikosR committed
3690

Mark Otto's avatar
Mark Otto committed
3691
3692
3693
  var isVertical          = placement === Tooltip.Direction.TOP || placement === Tooltip.Direction.BOTTOM
  var arrowDelta          = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
  var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
XhmikosR's avatar
XhmikosR committed
3694

Mark Otto's avatar
Mark Otto committed
3695
  $(tip).offset(offset)
XhmikosR's avatar
XhmikosR committed
3696

Mark Otto's avatar
Mark Otto committed
3697
3698
  this._replaceArrow(arrowDelta, tip[arrowOffsetPosition], isVertical)
}
XhmikosR's avatar
XhmikosR committed
3699
3700


Mark Otto's avatar
Mark Otto committed
3701
3702
3703
3704
3705
3706
3707
3708
/**
 * @param {number} delta
 * @param {number} dimension
 * @param {boolean} isHorizontal
 * @private
 */
Tooltip.prototype._replaceArrow = function (delta, dimension, isHorizontal) {
  var arrow = this.getArrowElement()
XhmikosR's avatar
XhmikosR committed
3709

Mark Otto's avatar
Mark Otto committed
3710
3711
3712
  arrow.style[isHorizontal ? 'left' : 'top'] =  50 * (1 - delta / dimension) + '%'
  arrow.style[isHorizontal ? 'top'  : 'left'] = ''
}
XhmikosR's avatar
XhmikosR committed
3713
3714
3715



Mark Otto's avatar
Mark Otto committed
3716
3717
3718
3719
3720
3721
3722
/**
 * @private
 */
Tooltip.prototype._fixTitle = function () {
  if (this.element.getAttribute('title') || typeof this.element.getAttribute('data-original-title') != 'string') {
    this.element.setAttribute('data-original-title', this.element.getAttribute('title') || '')
    this.element.setAttribute('title', '')
XhmikosR's avatar
XhmikosR committed
3723
  }
Mark Otto's avatar
Mark Otto committed
3724
}
XhmikosR's avatar
XhmikosR committed
3725
3726


Mark Otto's avatar
Mark Otto committed
3727
3728
3729
3730
3731
3732
3733
3734
/**
 * @param {Event=} opt_event
 * @param {Object=} opt_context
 * @private
 */
Tooltip.prototype._enter = function (opt_event, opt_context) {
  var dataKey = this.getDataKey()
  var context = opt_context || $(opt_event.currentTarget).data(dataKey)
XhmikosR's avatar
XhmikosR committed
3735

Mark Otto's avatar
Mark Otto committed
3736
3737
3738
3739
  if (context && context._tip && context._tip.offsetWidth) {
    context._hoverState = Tooltip._HoverState.IN
    return
  }
XhmikosR's avatar
XhmikosR committed
3740

Mark Otto's avatar
Mark Otto committed
3741
3742
3743
3744
  if (!context) {
    context = new this.constructor(opt_event.currentTarget, this._getDelegateConfig())
    $(opt_event.currentTarget).data(dataKey, context)
  }
XhmikosR's avatar
XhmikosR committed
3745

Mark Otto's avatar
Mark Otto committed
3746
  clearTimeout(context._timeout)
XhmikosR's avatar
XhmikosR committed
3747

Mark Otto's avatar
Mark Otto committed
3748
  context._hoverState = Tooltip._HoverState.IN
XhmikosR's avatar
XhmikosR committed
3749

Mark Otto's avatar
Mark Otto committed
3750
3751
3752
  if (!context.config['delay'] || !context.config['delay']['show']) {
    context['show']()
    return
XhmikosR's avatar
XhmikosR committed
3753
3754
  }

Mark Otto's avatar
Mark Otto committed
3755
3756
3757
3758
3759
3760
  context._timeout = setTimeout(function () {
    if (context._hoverState == Tooltip._HoverState.IN) {
      context['show']()
    }
  }, context.config['delay']['show'])
}
XhmikosR's avatar
XhmikosR committed
3761
3762


Mark Otto's avatar
Mark Otto committed
3763
3764
3765
3766
3767
3768
3769
3770
/**
 * @param {Event=} opt_event
 * @param {Object=} opt_context
 * @private
 */
Tooltip.prototype._leave = function (opt_event, opt_context) {
  var dataKey = this.getDataKey()
  var context = opt_context || $(opt_event.currentTarget).data(dataKey)
XhmikosR's avatar
XhmikosR committed
3771

Mark Otto's avatar
Mark Otto committed
3772
3773
3774
  if (!context) {
    context = new this.constructor(opt_event.currentTarget, this._getDelegateConfig())
    $(opt_event.currentTarget).data(dataKey, context)
XhmikosR's avatar
XhmikosR committed
3775
3776
  }

Mark Otto's avatar
Mark Otto committed
3777
  clearTimeout(context._timeout)
XhmikosR's avatar
XhmikosR committed
3778

Mark Otto's avatar
Mark Otto committed
3779
  context._hoverState = Tooltip._HoverState.OUT
XhmikosR's avatar
XhmikosR committed
3780

Mark Otto's avatar
Mark Otto committed
3781
3782
3783
  if (!context.config['delay'] || !context.config['delay']['hide']) {
    context['hide']()
    return
XhmikosR's avatar
XhmikosR committed
3784
3785
  }

Mark Otto's avatar
Mark Otto committed
3786
3787
3788
3789
3790
3791
  context._timeout = setTimeout(function () {
    if (context._hoverState == Tooltip._HoverState.OUT) {
      context['hide']()
    }
  }, context.config['delay']['hide'])
}
XhmikosR's avatar
XhmikosR committed
3792
3793
3794



Mark Otto's avatar
Mark Otto committed
3795
3796
3797
3798
3799
/**
 * ------------------------------------------------------------------------
 * jQuery Interface + noConflict implementaiton
 * ------------------------------------------------------------------------
 */
XhmikosR's avatar
XhmikosR committed
3800

Mark Otto's avatar
Mark Otto committed
3801
3802
3803
3804
3805
/**
 * @const
 * @type {Function}
 */
$.fn[Tooltip._NAME] = Tooltip._jQueryInterface
XhmikosR's avatar
XhmikosR committed
3806
3807


Mark Otto's avatar
Mark Otto committed
3808
3809
3810
3811
3812
/**
 * @const
 * @type {Function}
 */
$.fn[Tooltip._NAME]['Constructor'] = Tooltip
XhmikosR's avatar
XhmikosR committed
3813
3814


Mark Otto's avatar
Mark Otto committed
3815
3816
3817
3818
3819
3820
3821
3822
/**
 * @const
 * @type {Function}
 */
$.fn[Tooltip._NAME]['noConflict'] = function () {
  $.fn[Tooltip._NAME] = Tooltip._JQUERY_NO_CONFLICT
  return this
}
XhmikosR's avatar
XhmikosR committed
3823

Mark Otto's avatar
Mark Otto committed
3824
3825
3826
/** =======================================================================
 * Bootstrap: popover.js v4.0.0
 * http://getbootstrap.com/javascript/#popovers
XhmikosR's avatar
XhmikosR committed
3827
 * ========================================================================
Mark Otto's avatar
Mark Otto committed
3828
 * Copyright 2011-2015 Twitter, Inc.
XhmikosR's avatar
XhmikosR committed
3829
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
Mark Otto's avatar
Mark Otto committed
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
 * ========================================================================
 * @fileoverview - Bootstrap's popover plugin - extends tooltip.
 *
 * Public Methods & Properties:
 *
 *   + $.popover
 *   + $.popover.noConflict
 *   + $.popover.Constructor
 *   + $.popover.Constructor.VERSION
 *   + $.popover.Constructor.Defaults
 *   + $.popover.Constructor.Defaults.container
 *   + $.popover.Constructor.Defaults.animation
 *   + $.popover.Constructor.Defaults.placement
 *   + $.popover.Constructor.Defaults.selector
 *   + $.popover.Constructor.Defaults.template
 *   + $.popover.Constructor.Defaults.trigger
 *   + $.popover.Constructor.Defaults.title
 *   + $.popover.Constructor.Defaults.content
 *   + $.popover.Constructor.Defaults.delay
 *   + $.popover.Constructor.Defaults.html
 *   + $.popover.Constructor.Defaults.viewport
 *   + $.popover.Constructor.Defaults.viewport.selector
 *   + $.popover.Constructor.Defaults.viewport.padding
 *   + $.popover.Constructor.prototype.enable
 *   + $.popover.Constructor.prototype.disable
 *   + $.popover.Constructor.prototype.destroy
 *   + $.popover.Constructor.prototype.toggleEnabled
 *   + $.popover.Constructor.prototype.toggle
 *   + $.popover.Constructor.prototype.show
 *   + $.popover.Constructor.prototype.hide
 *
 * ========================================================================
 */
XhmikosR's avatar
XhmikosR committed
3863
3864


Mark Otto's avatar
Mark Otto committed
3865
'use strict';
XhmikosR's avatar
XhmikosR committed
3866
3867


Mark Otto's avatar
Mark Otto committed
3868
if (!Tooltip) throw new Error('Popover requires tooltip.js')
XhmikosR's avatar
XhmikosR committed
3869
3870


Mark Otto's avatar
Mark Otto committed
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
/**
 * Our tooltip class.
 * @param {Element!} element
 * @param {Object=} opt_config
 * @constructor
 * @extends {Tooltip}
 */
var Popover = function (element, opt_config) {
  Tooltip.apply(this, arguments)
}
Bootstrap.inherits(Popover, Tooltip)
XhmikosR's avatar
XhmikosR committed
3882
3883


Mark Otto's avatar
Mark Otto committed
3884
3885
3886
3887
3888
/**
 * @const
 * @type {string}
 */
Popover['VERSION'] = '4.0.0'
XhmikosR's avatar
XhmikosR committed
3889

XhmikosR's avatar
XhmikosR committed
3890

Mark Otto's avatar
Mark Otto committed
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
/**
 * @const
 * @type {Object}
 */
Popover['Defaults'] = $.extend({}, $.fn['tooltip']['Constructor']['Defaults'], {
  'placement': 'right',
  'trigger': 'click',
  'content': '',
  'template': '<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
})


/**
 * @const
 * @type {string}
 * @private
 */
Popover._NAME = 'popover'
XhmikosR's avatar
XhmikosR committed
3909
3910


Mark Otto's avatar
Mark Otto committed
3911
3912
3913
3914
3915
3916
/**
 * @const
 * @type {string}
 * @private
 */
Popover._DATA_KEY = 'bs.popover'
XhmikosR's avatar
XhmikosR committed
3917
3918


Mark Otto's avatar
Mark Otto committed
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
/**
 * @const
 * @enum {string}
 * @private
 */
Popover._Event = {
  HIDE   : 'hide.bs.popover',
  HIDDEN : 'hidden.bs.popover',
  SHOW   : 'show.bs.popover',
  SHOWN  : 'shown.bs.popover'
}
XhmikosR's avatar
XhmikosR committed
3930
3931


Mark Otto's avatar
Mark Otto committed
3932
3933
3934
3935
3936
3937
3938
3939
3940
/**
 * @const
 * @enum {string}
 * @private
 */
Popover._ClassName = {
  FADE : 'fade',
  IN  : 'in'
}
XhmikosR's avatar
XhmikosR committed
3941
3942


Mark Otto's avatar
Mark Otto committed
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
/**
 * @const
 * @enum {string}
 * @private
 */
Popover._Selector = {
  TITLE   : '.popover-title',
  CONTENT : '.popover-content',
  ARROW   : '.popover-arrow'
}
XhmikosR's avatar
XhmikosR committed
3953
3954


Mark Otto's avatar
Mark Otto committed
3955
3956
3957
3958
3959
3960
/**
 * @const
 * @type {Function}
 * @private
 */
Popover._JQUERY_NO_CONFLICT = $.fn[Popover._NAME]
XhmikosR's avatar
XhmikosR committed
3961
3962


Mark Otto's avatar
Mark Otto committed
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
/**
 * @param {Object|string=} opt_config
 * @this {jQuery}
 * @return {jQuery}
 * @private
 */
Popover._jQueryInterface = function (opt_config) {
  return this.each(function () {
    var data   = $(this).data(Popover._DATA_KEY)
    var config = typeof opt_config === 'object' ? opt_config : null
XhmikosR's avatar
XhmikosR committed
3973

Mark Otto's avatar
Mark Otto committed
3974
3975
    if (!data && opt_config === 'destroy') {
      return
XhmikosR's avatar
XhmikosR committed
3976
3977
    }

Mark Otto's avatar
Mark Otto committed
3978
3979
3980
3981
    if (!data) {
      data = new Popover(this, config)
      $(this).data(Popover._DATA_KEY, data)
    }
XhmikosR's avatar
XhmikosR committed
3982

Mark Otto's avatar
Mark Otto committed
3983
3984
3985
3986
3987
    if (typeof opt_config === 'string') {
      data[opt_config]()
    }
  })
}
Heinrich Fenkart's avatar
grunt    
Heinrich Fenkart committed
3988

XhmikosR's avatar
XhmikosR committed
3989

Mark Otto's avatar
Mark Otto committed
3990
3991
3992
3993
3994
3995
3996
/**
 * @return {string}
 * @protected
 */
Popover.prototype.getName = function () {
  return Popover._NAME
}
XhmikosR's avatar
XhmikosR committed
3997
3998


Mark Otto's avatar
Mark Otto committed
3999
4000
/**
 * @override
For faster browsing, not all history is shown. View entire blame