1/** 2 * @file 3 * bootstrap.js 4 * 5 * Provides general enhancements and fixes to Bootstrap's JS files. 6 */ 7 8var Drupal = Drupal || {}; 9 10(function($, Drupal){ 11 "use strict"; 12 13 Drupal.behaviors.bootstrap = { 14 attach: function(context) { 15 // Provide some Bootstrap tab/Drupal integration. 16 $(context).find('.tabbable').once('bootstrap-tabs', function () { 17 var $wrapper = $(this); 18 var $tabs = $wrapper.find('.nav-tabs'); 19 var $content = $wrapper.find('.tab-content'); 20 var borderRadius = parseInt($content.css('borderBottomRightRadius'), 10); 21 var bootstrapTabResize = function() { 22 if ($wrapper.hasClass('tabs-left') || $wrapper.hasClass('tabs-right')) { 23 $content.css('min-height', $tabs.outerHeight()); 24 } 25 }; 26 // Add min-height on content for left and right tabs. 27 bootstrapTabResize(); 28 // Detect tab switch. 29 if ($wrapper.hasClass('tabs-left') || $wrapper.hasClass('tabs-right')) { 30 $tabs.on('shown.bs.tab', 'a[data-toggle="tab"]', function (e) { 31 bootstrapTabResize(); 32 if ($wrapper.hasClass('tabs-left')) { 33 if ($(e.target).parent().is(':first-child')) { 34 $content.css('borderTopLeftRadius', '0'); 35 } 36 else { 37 $content.css('borderTopLeftRadius', borderRadius + 'px'); 38 } 39 } 40 else { 41 if ($(e.target).parent().is(':first-child')) { 42 $content.css('borderTopRightRadius', '0'); 43 } 44 else { 45 $content.css('borderTopRightRadius', borderRadius + 'px'); 46 } 47 } 48 }); 49 } 50 }); 51 } 52 }; 53 54 /** 55 * Behavior for . 56 */ 57 Drupal.behaviors.bootstrapFormHasError = { 58 attach: function (context, settings) { 59 if (settings.bootstrap && settings.bootstrap.formHasError) { 60 var $context = $(context); 61 $context.find('.form-item.has-error:not(.form-type-password.has-feedback)').once('error', function () { 62 var $formItem = $(this); 63 var $input = $formItem.find(':input'); 64 $input.on('keyup focus blur', function () { 65 var value = $input.val() || false; 66 $formItem[value ? 'removeClass' : 'addClass']('has-error'); 67 $input[value ? 'removeClass' : 'addClass']('error'); 68 }); 69 }); 70 } 71 } 72 }; 73 74 /** 75 * Bootstrap Popovers. 76 */ 77 Drupal.behaviors.bootstrapPopovers = { 78 attach: function (context, settings) { 79 // Immediately return if popovers are not available. 80 if (!settings.bootstrap || !settings.bootstrap.popoverEnabled || !$.fn.popover) { 81 return; 82 } 83 84 // Popover autoclose. 85 if (settings.bootstrap.popoverOptions.triggerAutoclose) { 86 var $currentPopover = null; 87 $(document) 88 .on('show.bs.popover', '[data-toggle=popover]', function () { 89 var $trigger = $(this); 90 var popover = $trigger.data('bs.popover'); 91 92 // Only keep track of clicked triggers that we're manually handling. 93 if (popover.options.originalTrigger === 'click') { 94 if ($currentPopover && !$currentPopover.is($trigger)) { 95 $currentPopover.popover('hide'); 96 } 97 $currentPopover = $trigger; 98 } 99 }) 100 .on('click', function (e) { 101 var $target = $(e.target); 102 var popover = $target.is('[data-toggle=popover]') && $target.data('bs.popover'); 103 if ($currentPopover && !$target.is('[data-toggle=popover]') && !$target.closest('.popover.in')[0]) { 104 $currentPopover.popover('hide'); 105 $currentPopover = null; 106 } 107 }) 108 ; 109 } 110 111 var elements = $(context).find('[data-toggle=popover]').toArray(); 112 for (var i = 0; i < elements.length; i++) { 113 var $element = $(elements[i]); 114 var options = $.extend({}, $.fn.popover.Constructor.DEFAULTS, settings.bootstrap.popoverOptions, $element.data()); 115 116 // Store the original trigger. 117 options.originalTrigger = options.trigger; 118 119 // If the trigger is "click", then we'll handle it manually here. 120 if (options.trigger === 'click') { 121 options.trigger = 'manual'; 122 } 123 124 // Retrieve content from a target element. 125 var $target = $(options.target || $element.is('a[href^="#"]') && $element.attr('href')).clone(); 126 if (!options.content && $target[0]) { 127 $target.removeClass('element-invisible hidden').removeAttr('aria-hidden'); 128 options.content = $target.wrap('<div/>').parent()[options.html ? 'html' : 'text']() || ''; 129 } 130 131 // Initialize the popover. 132 $element.popover(options); 133 134 // Handle clicks manually. 135 if (options.originalTrigger === 'click') { 136 // To ensure the element is bound multiple times, remove any 137 // previously set event handler before adding another one. 138 $element 139 .off('click.drupal.bootstrap.popover') 140 .on('click.drupal.bootstrap.popover', function (e) { 141 $(this).popover('toggle'); 142 e.preventDefault(); 143 e.stopPropagation(); 144 }) 145 ; 146 } 147 } 148 }, 149 detach: function (context, settings) { 150 // Immediately return if popovers are not available.
151 if (!settings.bootstrap || !settings.bootstrap.popoverEnabled || !$.fn.popover) { 152 return; 153 } 154 155 // Destroy all popovers. 156 $(context).find('[data-toggle="popover"]') 157 .off('click.drupal.bootstrap.popover') 158 .popover('destroy') 159 ; 160 } 161 }; 162 163 /** 164 * Bootstrap Tooltips. 165 */ 166 Drupal.behaviors.bootstrapTooltips = { 167 attach: function (context, settings) { 168 if (settings.bootstrap && settings.bootstrap.tooltipEnabled) { 169 var elements = $(context).find('[data-toggle="tooltip"]').toArray(); 170 for (var i = 0; i < elements.length; i++) { 171 var $element = $(elements[i]); 172 var options = $.extend({}, settings.bootstrap.tooltipOptions, $element.data()); 173 $element.tooltip(options); 174 } 175 } 176 } 177 }; 178 179 /** 180 * Anchor fixes. 181 */ 182 var $scrollableElement = $(); 183 Drupal.behaviors.bootstrapAnchors = { 184 attach: function(context, settings) { 185 var i, elements = ['html', 'body']; 186 if (!$scrollableElement.length) { 187 for (i = 0; i < elements.length; i++) { 188 var $element = $(elements[i]); 189 if ($element.scrollTop() > 0) { 190 $scrollableElement = $element; 191 break; 192 } 193 else { 194 $element.scrollTop(1); 195 if ($element.scrollTop() > 0) { 196 $element.scrollTop(0); 197 $scrollableElement = $element; 198 break; 199 } 200 } 201 } 202 } 203 if (!settings.bootstrap || settings.bootstrap.anchorsFix !== '1') { 204 return; 205 } 206 var anchors = $(context).find('a').toArray(); 207 for (i = 0; i < anchors.length; i++) { 208 if (!anchors[i].scrollTo) { 209 this.bootstrapAnchor(anchors[i]); 210 } 211 } 212 $scrollableElement.once('bootstrap-anchors', function () { 213 $scrollableElement.on('click.bootstrap-anchors', 'a[href*="#"]:not([data-toggle],[data-target],[data-slide])', function(e) { 214 if (this.scrollTo) { 215 this.scrollTo(e); 216 } 217 }); 218 }); 219 }, 220 bootstrapAnchor: function (element) { 221 element.validAnchor = element.nodeName === 'A' && (location.hostname === element.hostname || !element.hostname) && (element.hash.replace(/#/,'').length > 0); 222 element.scrollTo = function(event) { 223 var attr = 'id'; 224 var $target = $(element.hash); 225 // Check for anchors that use the name attribute instead. 226 if (!$target.length) { 227 attr = 'name'; 228 $target = $('[name="' + element.hash.replace('#', '') + '"]'); 229 } 230 // Immediately stop if no anchors are found. 231 if (!this.validAnchor && !$target.length) { 232 return; 233 } 234 // Anchor is valid, continue if there is an offset. 235 var offset = $target.offset().top - parseInt($scrollableElement.css('paddingTop'), 10) - parseInt($scrollableElement.css('marginTop'), 10); 236 if (offset > 0) { 237 if (event) { 238 event.preventDefault(); 239 } 240 var $fakeAnchor = $('<div/>') 241 .addClass('element-invisible') 242 .attr(attr, $target.attr(attr)) 243 .css({ 244 position: 'absolute', 245 top: offset + 'px', 246 zIndex: -1000 247 }) 248 .appendTo($scrollableElement); 249 $target.removeAttr(attr); 250 var complete = function () { 251 location.hash = element.hash; 252 $fakeAnchor.remove(); 253 $target.attr(attr, element.hash.replace('#', '')); 254 }; 255 if (Drupal.settings.bootstrap.anchorsSmoothScrolling) { 256 $scrollableElement.animate({ scrollTop: offset, avoidTransforms: true }, 400, complete); 257 } 258 else { 259 $scrollableElement.scrollTop(offset); 260 complete(); 261 } 262 } 263 }; 264 } 265 }; 266 267 /** 268 * Tabledrag theming elements. 269 */ 270 Drupal.theme.tableDragChangedMarker = function () { 271 return '<span class="tabledrag-changed glyphicon glyphicon-warning-sign text-warning"></span>'; 272 }; 273 274 Drupal.theme.tableDragChangedWarning = function () { 275 return '<div class="tabledrag-changed-warning alert alert-warning messages warning">' + Drupal.theme('tableDragChangedMarker') + ' ' + Drupal.t('Changes made in this table will not be saved until the form is submitted.') + '</div>'; 276 }; 277 278})(jQuery, Drupal);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.