Commit | Line | Data |
---|---|---|
1e7a45c4 VSB |
1 | /** |
2 | * @file | |
3 | * Bootstrap Popovers. | |
4 | */ | |
5 | ||
6 | var Drupal = Drupal || {}; | |
7 | ||
8 | (function ($, Drupal, Bootstrap) { | |
9 | "use strict"; | |
10 | ||
11 | var $document = $(document); | |
12 | ||
13 | /** | |
14 | * Extend the Bootstrap Popover plugin constructor class. | |
15 | */ | |
16 | Bootstrap.extendPlugin('popover', function (settings) { | |
17 | return { | |
18 | DEFAULTS: { | |
19 | animation: !!settings.popover_animation, | |
20 | autoClose: !!settings.popover_auto_close, | |
21 | enabled: settings.popover_enabled, | |
22 | html: !!settings.popover_html, | |
23 | placement: settings.popover_placement, | |
24 | selector: settings.popover_selector, | |
25 | trigger: settings.popover_trigger, | |
26 | title: settings.popover_title, | |
27 | content: settings.popover_content, | |
28 | delay: parseInt(settings.popover_delay, 10), | |
29 | container: settings.popover_container | |
30 | } | |
31 | }; | |
32 | }); | |
33 | ||
34 | /** | |
35 | * Bootstrap Popovers. | |
36 | * | |
37 | * @todo This should really be properly delegated if selector option is set. | |
38 | */ | |
39 | Drupal.behaviors.bootstrapPopovers = { | |
40 | $activePopover: null, | |
41 | attach: function (context) { | |
42 | // Immediately return if popovers are not available. | |
43 | if (!$.fn.popover || !$.fn.popover.Constructor.DEFAULTS.enabled) { | |
44 | return; | |
45 | } | |
46 | ||
47 | var _this = this; | |
48 | ||
49 | $document | |
50 | .on('show.bs.popover', '[data-toggle=popover]', function () { | |
51 | var $trigger = $(this); | |
52 | var popover = $trigger.data('bs.popover'); | |
53 | ||
54 | // Only keep track of clicked triggers that we're manually handling. | |
55 | if (popover.options.originalTrigger === 'click') { | |
56 | if (_this.$activePopover && _this.getOption('autoClose') && !_this.$activePopover.is($trigger)) { | |
57 | _this.$activePopover.popover('hide'); | |
58 | } | |
59 | _this.$activePopover = $trigger; | |
60 | } | |
61 | }) | |
62 | // Unfortunately, :focusable is only made available when using jQuery | |
63 | // UI. While this would be the most semantic pseudo selector to use | |
64 | // here, jQuery UI may not always be loaded. Instead, just use :visible | |
65 | // here as this just needs some sort of selector here. This activates | |
66 | // delegate binding to elements in jQuery so it can work it's bubbling | |
67 | // focus magic since elements don't really propagate their focus events. | |
68 | // @see https://www.drupal.org/project/bootstrap/issues/3013236 | |
69 | .on('focus.bs.popover', ':visible', function (e) { | |
70 | var $target = $(e.target); | |
71 | if (_this.$activePopover && _this.getOption('autoClose') && !_this.$activePopover.is($target) && !$target.closest('.popover.in')[0]) { | |
72 | _this.$activePopover.popover('hide'); | |
73 | _this.$activePopover = null; | |
74 | } | |
75 | }) | |
76 | .on('click.bs.popover', function (e) { | |
77 | var $target = $(e.target); | |
78 | if (_this.$activePopover && _this.getOption('autoClose') && !$target.is('[data-toggle=popover]') && !$target.closest('.popover.in')[0]) { | |
79 | _this.$activePopover.popover('hide'); | |
80 | _this.$activePopover = null; | |
81 | } | |
82 | }) | |
83 | .on('keyup.bs.popover', function (e) { | |
84 | if (_this.$activePopover && _this.getOption('autoClose') && e.which === 27) { | |
85 | _this.$activePopover.popover('hide'); | |
86 | _this.$activePopover = null; | |
87 | } | |
88 | }) | |
89 | ; | |
90 | ||
91 | var elements = $(context).find('[data-toggle=popover]').toArray(); | |
92 | for (var i = 0; i < elements.length; i++) { | |
93 | var $element = $(elements[i]); | |
94 | var options = $.extend({}, $.fn.popover.Constructor.DEFAULTS, $element.data()); | |
95 | ||
96 | // Store the original trigger. | |
97 | options.originalTrigger = options.trigger; | |
98 | ||
99 | // If the trigger is "click", then we'll handle it manually here. | |
100 | if (options.trigger === 'click') { | |
101 | options.trigger = 'manual'; | |
102 | } | |
103 | ||
104 | // Retrieve content from a target element. | |
105 | var target = options.target || $element.is('a[href^="#"]') && $element.attr('href'); | |
106 | var $target = $document.find(target).clone(); | |
107 | if (!options.content && $target[0]) { | |
108 | $target.removeClass('visually-hidden hidden').removeAttr('aria-hidden'); | |
109 | options.content = $target.wrap('<div/>').parent()[options.html ? 'html' : 'text']() || ''; | |
110 | } | |
111 | ||
112 | // Initialize the popover. | |
113 | $element.popover(options); | |
114 | ||
115 | // Handle clicks manually. | |
116 | if (options.originalTrigger === 'click') { | |
117 | // To ensure the element is bound multiple times, remove any | |
118 | // previously set event handler before adding another one. | |
119 | $element | |
120 | .off('click.drupal.bootstrap.popover') | |
121 | .on('click.drupal.bootstrap.popover', function (e) { | |
122 | $(this).popover('toggle'); | |
123 | e.preventDefault(); | |
124 | e.stopPropagation(); | |
125 | }) | |
126 | ; | |
127 | } | |
128 | } | |
129 | }, | |
130 | detach: function (context) { | |
131 | // Immediately return if popovers are not available. | |
132 | if (!$.fn.popover || !$.fn.popover.Constructor.DEFAULTS.enabled) { | |
133 | return; | |
134 | } | |
135 | ||
136 | // Destroy all popovers. | |
137 | $(context).find('[data-toggle="popover"]') | |
138 | .off('click.drupal.bootstrap.popover') | |
139 | .popover('destroy') | |
140 | ; | |
141 | }, | |
142 | getOption: function(name, defaultValue, element) { | |
143 | var $element = element ? $(element) : this.$activePopover; | |
144 | var options = $.extend(true, {}, $.fn.popover.Constructor.DEFAULTS, ($element && $element.data('bs.popover') || {}).options); | |
145 | if (options[name] !== void 0) { | |
146 | return options[name]; | |
147 | } | |
148 | return defaultValue !== void 0 ? defaultValue : void 0; | |
149 | } | |
150 | }; | |
151 | ||
152 | })(window.jQuery, window.Drupal, window.Drupal.bootstrap); |