1/** 2 * @file 3 * JavaScript behaviors for Select2 integration. 4 */ 5 6(function ($, Drupal, once) { 7 8 'use strict'; 9 10 // @see https://select2.github.io/options.html 11 Drupal.webform = Drupal.webform || {}; 12 Drupal.webform.select2 = Drupal.webform.select2 || {}; 13 Drupal.webform.select2.options = Drupal.webform.select2.options || {}; 14 Drupal.webform.select2.options.width = Drupal.webform.select2.options.width || '100%'; 15 Drupal.webform.select2.options.widthInline = Drupal.webform.select2.options.widthInline || '50%'; 16 17 /** 18 * Initialize Select2 support. 19 * 20 * @type {Drupal~behavior} 21 */ 22 Drupal.behaviors.webformSelect2 = { 23 attach: function (context) { 24 if (!$.fn.select2) { 25 return; 26 } 27 28 $(once('webform-select2', 'select.js-webform-select2, .js-webform-select2 select', context)) 29 .each(function () { 30 var $select = $(this); 31 32 var options = {}; 33 if ($select.parents('.webform-element--title-inline').length) { 34 options.width = Drupal.webform.select2.options.widthInline; 35 } 36 options = $.extend(options, Drupal.webform.select2.options); 37 if ($select.data('placeholder')) { 38 options.placeholder = $select.data('placeholder'); 39 if (!$select.prop('multiple')) { 40 // Allow single option to be deselected. 41 options.allowClear = true; 42 } 43 } 44 if ($select.data('limit')) { 45 options.maximumSelectionLength = $select.data('limit'); 46 } 47 48 // Remove required attribute from IE11 which breaks 49 // HTML5 clientside validation. 50 // @see https://github.com/select2/select2/issues/5114 51 if (window.navigator.userAgent.indexOf('Trident/') !== -1 52 && $select.attr('multiple') 53 && $select.attr('required')) { 54 $select.removeAttr('required'); 55 } 56 57 $select.select2(options); 58 }); 59 60 } 61 }; 62 63 /** 64 * ISSUE: 65 * Hiding/showing element via #states API cause select2 dropdown to appear in the wrong position. 66 * 67 * WORKAROUND: 68 * Close (aka hide) select2 dropdown when #states API hides or shows an element. 69 * 70 * Steps to reproduce: 71 * - Add custom 'Submit button(s)' 72 * - Hide submit button 73 * - Save 74 * - Open 'Submit button(s)' dialog 75 * 76 * Dropdown body is positioned incorrectly when dropdownParent isn't statically positioned. 77 * @see https://github.com/select2/select2/issues/3303 78 */ 79 $(function () { 80 if ($.fn.select2) { 81 $(document).on('state:visible state:visible-slide', function (e) { 82 $('select.select2-hidden-accessible').select2('close'); 83 }); 84 } 85 86 // Select2 search broken inside jQuery UI 1.10.x modal Dialog. 87 // @see https://github.com/select2/select2/issues/1246 88 if ($.ui && $.ui.dialog && $.ui.dialog.prototype._allowInteraction) { 89 var ui_dialog_interaction = $.ui.dialog.prototype._allowInteraction; 90 $.ui.dialog.prototype._allowInteraction = function (e) { 91 if ($(e.target).closest('.select2-dropdown').length) { 92 return true; 93 } 94 return ui_dialog_interaction.apply(this, arguments); 95 }; 96 } 97 }); 98 99})(jQuery, Drupal, once); 100; 101/** 102 * @file 103 * Some basic behaviors and utility functions for Views. 104 */ 105 106(function ($, Drupal, drupalSettings) { 107 /** 108 * @namespace 109 */ 110 Drupal.Views = {}; 111 112 /** 113 * Helper function to parse a querystring. 114 * 115 * @param {string} query 116 * The querystring to parse. 117 * 118 * @return {object} 119 * A map of query parameters. 120 */ 121 Drupal.Views.parseQueryString = function (query) { 122 const args = {}; 123 const pos = query.indexOf('?'); 124 if (pos !== -1) { 125 query = query.substring(pos + 1); 126 } 127 let pair; 128 const pairs = query.split('&'); 129 for (let i = 0; i < pairs.length; i++) { 130 pair = pairs[i].split('='); 131 // Ignore the 'q' path argument, if present. 132 if (pair[0] !== 'q' && pair[1]) { 133 args[decodeURIComponent(pair[0].replace(/\+/g, ' '))] = 134 decodeURIComponent(pair[1].replace(/\+/g, ' ')); 135 } 136 } 137 return args; 138 }; 139 140 /** 141 * Helper function to return a view's arguments based on a path. 142 * 143 * @param {string} href 144 * The href to check. 145 * @param {string} viewPath 146 * The views path to check. 147 * 148 * @return {object}
149 * An object containing `view_args` and `view_path`. 150 */ 151 Drupal.Views.parseViewArgs = function (href, viewPath) { 152 const returnObj = {}; 153 const path = Drupal.Views.getPath(href); 154 // Get viewPath url without baseUrl portion. 155 const viewHref = Drupal.url(viewPath).substring( 156 drupalSettings.path.baseUrl.length, 157 ); 158 // Ensure we have a correct path. 159 if (viewHref && path.substring(0, viewHref.length + 1) === `${viewHref}/`) { 160 returnObj.view_args = decodeURIComponent( 161 path.substring(viewHref.length + 1, path.length), 162 ); 163 returnObj.view_path = path; 164 } 165 return returnObj; 166 }; 167 168 /** 169 * Strip off the protocol plus domain from an href. 170 * 171 * @param {string} href 172 * The href to strip. 173 * 174 * @return {string} 175 * The href without the protocol and domain. 176 */ 177 Drupal.Views.pathPortion = function (href) { 178 // Remove e.g. http://example.com if present. 179 const protocol = window.location.protocol; 180 if (href.substring(0, protocol.length) === protocol) { 181 // 2 is the length of the '//' that normally follows the protocol. 182 href = href.substring(href.indexOf('/', protocol.length + 2)); 183 } 184 return href; 185 }; 186 187 /** 188 * Return the Drupal path portion of an href. 189 * 190 * @param {string} href 191 * The href to check. 192 * 193 * @return {string} 194 * An internal path. 195 */ 196 Drupal.Views.getPath = function (href) { 197 href = Drupal.Views.pathPortion(href); 198 href = href.substring(drupalSettings.path.baseUrl.length, href.length); 199 // 3 is the length of the '?q=' added to the url without clean urls. 200 if (href.substring(0, 3) === '?q=') { 201 href = href.substring(3, href.length); 202 } 203 const chars = ['#', '?', '&']; 204 for (let i = 0; i < chars.length; i++) { 205 if (href.indexOf(chars[i]) > -1) { 206 href = href.substr(0, href.indexOf(chars[i])); 207 } 208 } 209 return href; 210 }; 211})(jQuery, Drupal, drupalSettings); 212; 213/** 214 * @file 215 * Handles AJAX fetching of views, including filter submission and response. 216 */ 217 218(function ($, Drupal, drupalSettings) { 219 /** 220 * Attaches the AJAX behavior to exposed filters forms and key View links. 221 * 222 * @type {Drupal~behavior} 223 * 224 * @prop {Drupal~behaviorAttach} attach 225 * Attaches ajaxView functionality to relevant elements. 226 */ 227 Drupal.behaviors.ViewsAjaxView = {}; 228 Drupal.behaviors.ViewsAjaxView.attach = function (context, settings) { 229 if (settings && settings.views && settings.views.ajaxViews) { 230 const { 231 views: { ajaxViews }, 232 } = settings; 233 Object.keys(ajaxViews || {}).forEach((i) => { 234 Drupal.views.instances[i] = new Drupal.views.ajaxView(ajaxViews[i]); 235 }); 236 } 237 }; 238 Drupal.behaviors.ViewsAjaxView.detach = (context, settings, trigger) => { 239 if (trigger === 'unload') { 240 if (settings && settings.views && settings.views.ajaxViews) { 241 const { 242 views: { ajaxViews }, 243 } = settings; 244 Object.keys(ajaxViews || {}).forEach((i) => { 245 const selector = `.js-view-dom-id-${ajaxViews[i].view_dom_id}`; 246 if ($(selector, context).length) { 247 delete Drupal.views.instances[i]; 248 delete settings.views.ajaxViews[i]; 249 } 250 }); 251 } 252 } 253 }; 254 255 /** 256 * @namespace 257 */ 258 Drupal.views = {}; 259 260 /** 261 * @type {object.<string, Drupal.views.ajaxView>} 262 */ 263 Drupal.views.instances = {}; 264 265 /** 266 * JavaScript object for a certain view. 267 * 268 * @constructor 269 * 270 * @param {object} settings 271 * Settings object for the ajax view. 272 * @param {string} settings.view_dom_id 273 * The DOM id of the view. 274 */ 275 Drupal.views.ajaxView = function (settings) { 276 const selector = `.js-view-dom-id-${settings.view_dom_id}`; 277 this.$view = $(selector); 278 279 // Retrieve the path to use for views' ajax. 280 let ajaxPath = drupalSettings.views.ajax_path; 281 282 // If there are multiple views this might've ended up showing up multiple 283 // times. 284 if (ajaxPath.constructor.toString().indexOf('Array') !== -1) { 285 ajaxPath = ajaxPath[0]; 286 } 287
288 // Check if there are any GET parameters to send to views. 289 let queryString = window.location.search || ''; 290 if (queryString !== '') { 291 // Remove the question mark and Drupal path component if any. 292 queryString = queryString 293 .slice(1) 294 .replace(/q=[^&]+&?|&?render=[^&]+/, ''); 295 if (queryString !== '') { 296 // If there is a '?' in ajaxPath, clean url are on and & should be 297 // used to add parameters. 298 queryString = (/\?/.test(ajaxPath) ? '&' : '?') + queryString; 299 } 300 } 301 302 this.element_settings = { 303 url: ajaxPath + queryString, 304 submit: settings, 305 setClick: true, 306 event: 'click', 307 selector, 308 progress: { type: 'fullscreen' }, 309 }; 310 311 this.settings = settings; 312 313 // Add the ajax to exposed forms. 314 this.$exposed_form = $( 315 `form#views-exposed-form-${settings.view_name.replace( 316 /_/g, 317 '-', 318 )}-${settings.view_display_id.replace(/_/g, '-')}`, 319 ); 320 once('exposed-form', this.$exposed_form).forEach( 321 $.proxy(this.attachExposedFormAjax, this), 322 ); 323 324 // Add the ajax to pagers. 325 once( 326 'ajax-pager', 327 this.$view 328 // Don't attach to nested views. Doing so would attach multiple behaviors 329 // to a given element. 330 .filter($.proxy(this.filterNestedViews, this)), 331 ).forEach($.proxy(this.attachPagerAjax, this)); 332 333 // Add a trigger to update this view specifically. In order to trigger a 334 // refresh use the following code. 335 // 336 // @code 337 // $('.view-name').trigger('RefreshView'); 338 // @endcode 339 const selfSettings = $.extend({}, this.element_settings, { 340 event: 'RefreshView', 341 base: this.selector, 342 element: this.$view.get(0), 343 }); 344 this.refreshViewAjax = Drupal.ajax(selfSettings); 345 }; 346 347 /** 348 * @method 349 */ 350 Drupal.views.ajaxView.prototype.attachExposedFormAjax = function () { 351 const that = this; 352 this.exposedFormAjax = []; 353 // Exclude the reset buttons so no AJAX behaviors are bound. Many things 354 // break during the form reset phase if using AJAX. 355 $( 356 'input[type=submit], button[type=submit], input[type=image]', 357 this.$exposed_form, 358 ) 359 .not('[data-drupal-selector=edit-reset]') 360 .each(function (index) { 361 const selfSettings = $.extend({}, that.element_settings, { 362 base: $(this).attr('id'), 363 element: this, 364 }); 365 that.exposedFormAjax[index] = Drupal.ajax(selfSettings); 366 }); 367 }; 368 369 /** 370 * @return {boolean} 371 * If there is at least one parent with a view class return false. 372 */ 373 Drupal.views.ajaxView.prototype.filterNestedViews = function () { 374 // If there is at least one parent with a view class, this view 375 // is nested (e.g., an attachment). Bail. 376 return !this.$view.parents('.view').length; 377 }; 378 379 /** 380 * Attach the ajax behavior to each link. 381 */ 382 Drupal.views.ajaxView.prototype.attachPagerAjax = function () { 383 this.$view 384 .find( 385 'ul.js-pager__items > li > a, th.views-field a, .attachment .views-summary a', 386 ) 387 .each($.proxy(this.attachPagerLinkAjax, this)); 388 }; 389 390 /** 391 * Attach the ajax behavior to a singe link. 392 * 393 * @param {string} [id] 394 * The ID of the link. 395 * @param {HTMLElement} link 396 * The link element. 397 */ 398 Drupal.views.ajaxView.prototype.attachPagerLinkAjax = function (id, link) { 399 const $link = $(link); 400 const viewData = {}; 401 const href = $link.attr('href'); 402 // Construct an object using the settings defaults and then overriding 403 // with data specific to the link. 404 $.extend( 405 viewData, 406 this.settings, 407 Drupal.Views.parseQueryString(href), 408 // Extract argument data from the URL. 409 Drupal.Views.parseViewArgs(href, this.settings.view_base_path), 410 ); 411 412 const selfSettings = $.extend({}, this.element_settings, {
413 submit: viewData, 414 base: false, 415 element: link, 416 }); 417 this.pagerAjax = Drupal.ajax(selfSettings); 418 }; 419 420 /** 421 * Views scroll to top ajax command. 422 * 423 * @param {Drupal.Ajax} [ajax] 424 * A {@link Drupal.ajax} object. 425 * @param {object} response 426 * Ajax response. 427 * @param {string} response.selector 428 * Selector to use. 429 */ 430 Drupal.AjaxCommands.prototype.viewsScrollTop = function (ajax, response) { 431 // Scroll to the top of the view. This will allow users 432 // to browse newly loaded content after e.g. clicking a pager 433 // link. 434 const offset = $(response.selector).offset(); 435 // We can't guarantee that the scrollable object should be 436 // the body, as the view could be embedded in something 437 // more complex such as a modal popup. Recurse up the DOM 438 // and scroll the first element that has a non-zero top. 439 let scrollTarget = response.selector; 440 while ($(scrollTarget).scrollTop() === 0 && $(scrollTarget).parent()) { 441 scrollTarget = $(scrollTarget).parent(); 442 } 443 // Only scroll upward. 444 if (offset.top - 10 < $(scrollTarget).scrollTop()) { 445 $(scrollTarget).animate({ scrollTop: offset.top - 10 }, 500); 446 } 447 }; 448})(jQuery, Drupal, drupalSettings); 449; 450/** 451 * @file 452 * Adds the active class to active links, for proper top bar rendering. 453 * 454 */ 455(function ($, Drupal) { 456 /** 457 * Adds the "active" class to top bar <li> elements with active child links. 458 */ 459 Drupal.behaviors.foundationTopBarActive = { 460 attach: function (context, settings) { 461 var $active_links = $(context).find('.top-bar .menu-item > a.is-active'); 462 if ($active_links.length) { 463 $(once('foundationTopBarActive', $active_links)).each(function() { 464 $(this).parent().addClass('active'); 465 }); 466 } 467 } 468 }; 469 470})(jQuery, Drupal); 471;
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.