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