153 lines
5.5 KiB
JavaScript
153 lines
5.5 KiB
JavaScript
/**
|
|
* @file
|
|
* Bootstrap Popovers.
|
|
*/
|
|
|
|
var Drupal = Drupal || {};
|
|
|
|
(function ($, Drupal, Bootstrap) {
|
|
"use strict";
|
|
|
|
var $document = $(document);
|
|
|
|
/**
|
|
* Extend the Bootstrap Popover plugin constructor class.
|
|
*/
|
|
Bootstrap.extendPlugin('popover', function (settings) {
|
|
return {
|
|
DEFAULTS: {
|
|
animation: !!settings.popover_animation,
|
|
autoClose: !!settings.popover_auto_close,
|
|
enabled: settings.popover_enabled,
|
|
html: !!settings.popover_html,
|
|
placement: settings.popover_placement,
|
|
selector: settings.popover_selector,
|
|
trigger: settings.popover_trigger,
|
|
title: settings.popover_title,
|
|
content: settings.popover_content,
|
|
delay: parseInt(settings.popover_delay, 10),
|
|
container: settings.popover_container
|
|
}
|
|
};
|
|
});
|
|
|
|
/**
|
|
* Bootstrap Popovers.
|
|
*
|
|
* @todo This should really be properly delegated if selector option is set.
|
|
*/
|
|
Drupal.behaviors.bootstrapPopovers = {
|
|
$activePopover: null,
|
|
attach: function (context) {
|
|
// Immediately return if popovers are not available.
|
|
if (!$.fn.popover || !$.fn.popover.Constructor.DEFAULTS.enabled) {
|
|
return;
|
|
}
|
|
|
|
var _this = this;
|
|
|
|
$document
|
|
.on('show.bs.popover', '[data-toggle=popover]', function () {
|
|
var $trigger = $(this);
|
|
var popover = $trigger.data('bs.popover');
|
|
|
|
// Only keep track of clicked triggers that we're manually handling.
|
|
if (popover.options.originalTrigger === 'click') {
|
|
if (_this.$activePopover && _this.getOption('autoClose') && !_this.$activePopover.is($trigger)) {
|
|
_this.$activePopover.popover('hide');
|
|
}
|
|
_this.$activePopover = $trigger;
|
|
}
|
|
})
|
|
// Unfortunately, :focusable is only made available when using jQuery
|
|
// UI. While this would be the most semantic pseudo selector to use
|
|
// here, jQuery UI may not always be loaded. Instead, just use :visible
|
|
// here as this just needs some sort of selector here. This activates
|
|
// delegate binding to elements in jQuery so it can work it's bubbling
|
|
// focus magic since elements don't really propagate their focus events.
|
|
// @see https://www.drupal.org/project/bootstrap/issues/3013236
|
|
.on('focus.bs.popover', ':visible', function (e) {
|
|
var $target = $(e.target);
|
|
if (_this.$activePopover && _this.getOption('autoClose') && !_this.$activePopover.is($target) && !$target.closest('.popover.in')[0]) {
|
|
_this.$activePopover.popover('hide');
|
|
_this.$activePopover = null;
|
|
}
|
|
})
|
|
.on('click.bs.popover', function (e) {
|
|
var $target = $(e.target);
|
|
if (_this.$activePopover && _this.getOption('autoClose') && !$target.is('[data-toggle=popover]') && !$target.closest('.popover.in')[0]) {
|
|
_this.$activePopover.popover('hide');
|
|
_this.$activePopover = null;
|
|
}
|
|
})
|
|
.on('keyup.bs.popover', function (e) {
|
|
if (_this.$activePopover && _this.getOption('autoClose') && e.which === 27) {
|
|
_this.$activePopover.popover('hide');
|
|
_this.$activePopover = null;
|
|
}
|
|
})
|
|
;
|
|
|
|
var elements = $(context).find('[data-toggle=popover]').toArray();
|
|
for (var i = 0; i < elements.length; i++) {
|
|
var $element = $(elements[i]);
|
|
var options = $.extend({}, $.fn.popover.Constructor.DEFAULTS, $element.data());
|
|
|
|
// Store the original trigger.
|
|
options.originalTrigger = options.trigger;
|
|
|
|
// If the trigger is "click", then we'll handle it manually here.
|
|
if (options.trigger === 'click') {
|
|
options.trigger = 'manual';
|
|
}
|
|
|
|
// Retrieve content from a target element.
|
|
var target = options.target || $element.is('a[href^="#"]') && $element.attr('href');
|
|
var $target = $document.find(target).clone();
|
|
if (!options.content && $target[0]) {
|
|
$target.removeClass('visually-hidden hidden').removeAttr('aria-hidden');
|
|
options.content = $target.wrap('<div/>').parent()[options.html ? 'html' : 'text']() || '';
|
|
}
|
|
|
|
// Initialize the popover.
|
|
$element.popover(options);
|
|
|
|
// Handle clicks manually.
|
|
if (options.originalTrigger === 'click') {
|
|
// To ensure the element is bound multiple times, remove any
|
|
// previously set event handler before adding another one.
|
|
$element
|
|
.off('click.drupal.bootstrap.popover')
|
|
.on('click.drupal.bootstrap.popover', function (e) {
|
|
$(this).popover('toggle');
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
})
|
|
;
|
|
}
|
|
}
|
|
},
|
|
detach: function (context) {
|
|
// Immediately return if popovers are not available.
|
|
if (!$.fn.popover || !$.fn.popover.Constructor.DEFAULTS.enabled) {
|
|
return;
|
|
}
|
|
|
|
// Destroy all popovers.
|
|
$(context).find('[data-toggle="popover"]')
|
|
.off('click.drupal.bootstrap.popover')
|
|
.popover('destroy')
|
|
;
|
|
},
|
|
getOption: function(name, defaultValue, element) {
|
|
var $element = element ? $(element) : this.$activePopover;
|
|
var options = $.extend(true, {}, $.fn.popover.Constructor.DEFAULTS, ($element && $element.data('bs.popover') || {}).options);
|
|
if (options[name] !== void 0) {
|
|
return options[name];
|
|
}
|
|
return defaultValue !== void 0 ? defaultValue : void 0;
|
|
}
|
|
};
|
|
|
|
})(window.jQuery, window.Drupal, window.Drupal.bootstrap);
|