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 if (settings.bootstrap && settings.bootstrap.popoverEnabled) { 80 var $currentPopover = $(); 81 if (settings.bootstrap.popoverOptions.triggerAutoclose) { 82 $(document).on('click', function (e) { 83 if ($currentPopover.length && !$(e.target).is('[data-toggle=popover]') && $(e.target).parents('.popover.in').length === 0) { 84 $currentPopover.popover('hide'); 85 $currentPopover = $(); 86 } 87 }); 88 } 89 var elements = $(context).find('[data-toggle=popover]').toArray(); 90 for (var i = 0; i < elements.length; i++) { 91 var $element = $(elements[i]); 92 var options = $.extend({}, settings.bootstrap.popoverOptions, $element.data()); 93 if (!options.content) { 94 options.content = function () { 95 var target = $(this).data('target'); 96 return target && $(target) && $(target).length && $(target).clone().removeClass('element-invisible').wrap('<div/>').parent()[$(this).data('bs.popover').options.html ? 'html' : 'text']() || ''; 97 } 98 } 99 $element.popover(options).on('click', function (e) { 100 e.preventDefault(); 101 }); 102 if (settings.bootstrap.popoverOptions.triggerAutoclose) { 103 $element.on('show.bs.popover', function () { 104 if ($currentPopover.length) { 105 $currentPopover.popover('hide'); 106 } 107 $currentPopover = $(this); 108 }); 109 } 110 } 111 } 112 } 113 }; 114 115 /** 116 * Bootstrap Tooltips. 117 */ 118 Drupal.behaviors.bootstrapTooltips = { 119 attach: function (context, settings) { 120 if (settings.bootstrap && settings.bootstrap.tooltipEnabled) { 121 var elements = $(context).find('[data-toggle="tooltip"]').toArray(); 122 for (var i = 0; i < elements.length; i++) { 123 var $element = $(elements[i]); 124 var options = $.extend({}, settings.bootstrap.tooltipOptions, $element.data()); 125 $element.tooltip(options); 126 } 127 } 128 } 129 }; 130 131 /** 132 * Anchor fixes. 133 */ 134 var $scrollableElement = $();
135 Drupal.behaviors.bootstrapAnchors = { 136 attach: function(context, settings) { 137 var i, elements = ['html', 'body']; 138 if (!$scrollableElement.length) { 139 for (i = 0; i < elements.length; i++) { 140 var $element = $(elements[i]); 141 if ($element.scrollTop() > 0) { 142 $scrollableElement = $element; 143 break; 144 } 145 else { 146 $element.scrollTop(1); 147 if ($element.scrollTop() > 0) { 148 $element.scrollTop(0); 149 $scrollableElement = $element; 150 break; 151 } 152 } 153 } 154 } 155 if (!settings.bootstrap || settings.bootstrap.anchorsFix !== '1') { 156 return; 157 } 158 var anchors = $(context).find('a').toArray(); 159 for (i = 0; i < anchors.length; i++) { 160 if (!anchors[i].scrollTo) { 161 this.bootstrapAnchor(anchors[i]); 162 } 163 } 164 $scrollableElement.once('bootstrap-anchors', function () { 165 $scrollableElement.on('click.bootstrap-anchors', 'a[href*="#"]:not([data-toggle],[data-target],[data-slide])', function(e) { 166 if (this.scrollTo) { 167 this.scrollTo(e); 168 } 169 }); 170 }); 171 }, 172 bootstrapAnchor: function (element) { 173 element.validAnchor = element.nodeName === 'A' && (location.hostname === element.hostname || !element.hostname) && (element.hash.replace(/#/,'').length > 0); 174 element.scrollTo = function(event) { 175 var attr = 'id'; 176 var $target = $(element.hash); 177 // Check for anchors that use the name attribute instead. 178 if (!$target.length) { 179 attr = 'name'; 180 $target = $('[name="' + element.hash.replace('#', '') + '"]'); 181 } 182 // Immediately stop if no anchors are found. 183 if (!this.validAnchor && !$target.length) { 184 return; 185 } 186 // Anchor is valid, continue if there is an offset. 187 var offset = $target.offset().top - parseInt($scrollableElement.css('paddingTop'), 10) - parseInt($scrollableElement.css('marginTop'), 10); 188 if (offset > 0) { 189 if (event) { 190 event.preventDefault(); 191 } 192 var $fakeAnchor = $('<div/>') 193 .addClass('element-invisible') 194 .attr(attr, $target.attr(attr)) 195 .css({ 196 position: 'absolute', 197 top: offset + 'px', 198 zIndex: -1000 199 }) 200 .appendTo($scrollableElement); 201 $target.removeAttr(attr); 202 var complete = function () { 203 location.hash = element.hash; 204 $fakeAnchor.remove(); 205 $target.attr(attr, element.hash.replace('#', '')); 206 }; 207 if (Drupal.settings.bootstrap.anchorsSmoothScrolling) { 208 $scrollableElement.animate({ scrollTop: offset, avoidTransforms: true }, 400, complete); 209 } 210 else { 211 $scrollableElement.scrollTop(offset); 212 complete(); 213 } 214 } 215 }; 216 } 217 }; 218 219 /** 220 * Tabledrag theming elements. 221 */ 222 Drupal.theme.tableDragChangedMarker = function () { 223 return '<span class="tabledrag-changed glyphicon glyphicon-warning-sign text-warning"></span>'; 224 }; 225 226 Drupal.theme.tableDragChangedWarning = function () { 227 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>'; 228 }; 229 230})(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.