From: Jeff Veit Date: Tue, 13 Nov 2018 18:25:05 +0000 (+0000) Subject: Updated the Bootstrap theme. X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs-website;a=commitdiff_plain;h=dd08b95e4e519a02d45a50fb504bf5d685eaa9e3 Updated the Bootstrap theme. --- diff --git a/web/themes/contrib/bootstrap/bootstrap.info.yml b/web/themes/contrib/bootstrap/bootstrap.info.yml index d613fe3bf..b464980d9 100644 --- a/web/themes/contrib/bootstrap/bootstrap.info.yml +++ b/web/themes/contrib/bootstrap/bootstrap.info.yml @@ -73,8 +73,8 @@ libraries-override: theme: css/node.preview.css: false -# Information added by Drupal.org packaging script on 2018-03-11 -version: '8.x-3.11' +# Information added by Drupal.org packaging script on 2018-11-12 +version: '8.x-3.14' core: '8.x' project: 'bootstrap' -datestamp: 1520792888 +datestamp: 1542006206 diff --git a/web/themes/contrib/bootstrap/js/attributes.js b/web/themes/contrib/bootstrap/js/attributes.js index e0c0c441f..ced3356b1 100644 --- a/web/themes/contrib/bootstrap/js/attributes.js +++ b/web/themes/contrib/bootstrap/js/attributes.js @@ -345,17 +345,17 @@ identifier = identifier.toLowerCase(); if (filter['__'] === void 0) { - identifier = identifier.replace('__', '#DOUBLE_UNDERSCORE#', identifier); + identifier = identifier.replace('__', '#DOUBLE_UNDERSCORE#'); } - identifier = identifier.replace(Object.keys(filter), Object.keys(filter).map(function(key) { return filter[key]; }), identifier); + identifier = identifier.replace(Object.keys(filter), Object.keys(filter).map(function(key) { return filter[key]; })); if (filter['__'] === void 0) { - identifier = identifier.replace('#DOUBLE_UNDERSCORE#', '__', identifier); + identifier = identifier.replace('#DOUBLE_UNDERSCORE#', '__'); } identifier = identifier.replace(/[^\u002D\u0030-\u0039\u0041-\u005A\u005F\u0061-\u007A\u00A1-\uFFFF]/g, ''); - identifier = identifier.replace(['/^[0-9]/', '/^(-[0-9])|^(--)/'], ['_', '__'], identifier); + identifier = identifier.replace(['/^[0-9]/', '/^(-[0-9])|^(--)/'], ['_', '__']); return identifier; }; diff --git a/web/themes/contrib/bootstrap/js/drupal.bootstrap.js b/web/themes/contrib/bootstrap/js/drupal.bootstrap.js index 07af6a35c..573afd37f 100644 --- a/web/themes/contrib/bootstrap/js/drupal.bootstrap.js +++ b/web/themes/contrib/bootstrap/js/drupal.bootstrap.js @@ -353,14 +353,17 @@ /** * Simulates a native event on an element in the browser. * - * Note: This is a pretty complete modern implementation. If things are quite - * working the way you intend (in older browsers), you may wish to use the - * jQuery.simulate plugin. If it's available, this method will defer to it. + * Note: This is a fairly complete modern implementation. If things aren't + * working quite the way you intend (in older browsers), you may wish to use + * the jQuery.simulate plugin. If it's available, this method will defer to + * that plugin. * * @see https://github.com/jquery/jquery-simulate * - * @param {HTMLElement} element - * A DOM element to dispatch event on. + * @param {HTMLElement|jQuery} element + * A DOM element to dispatch event on. Note: this may be a jQuery object, + * however be aware that this will trigger the same event for each element + * inside the jQuery collection; use with caution. * @param {String} type * The type of event to simulate. * @param {Object} [options] @@ -368,13 +371,36 @@ * an event is being proxied, you should just pass the original event * object here. This allows, if the browser supports it, to be a truly * simulated event. + * + * @return {Boolean} + * The return value is false if event is cancelable and at least one of the + * event handlers which handled this event called Event.preventDefault(). + * Otherwise it returns true. */ Bootstrap.simulate = function (element, type, options) { + // Handle jQuery object wrappers so it triggers on each element. + if (element instanceof $) { + var ret = true; + element.each(function () { + if (!Bootstrap.simulate(this, type, options)) { + ret = false; + } + }); + return ret; + } + + if (!(element instanceof HTMLElement)) { + this.fatal('Passed element must be an instance of HTMLElement, got "@type" instead.', { + '@type': typeof element, + }); + } + // Defer to the jQuery.simulate plugin, if it's available. if (typeof $.simulate === 'function') { new $.simulate(element, type, options); - return; + return true; } + var event; var ctor; for (var name in this.eventMap) { @@ -398,20 +424,44 @@ } if (typeof window[ctor] === 'function') { event = new window[ctor](type, opts); - element.dispatchEvent(event); + return element.dispatchEvent(event); } else if (document.createEvent) { event = document.createEvent(ctor); event.initEvent(type, opts.bubbles, opts.cancelable); - element.dispatchEvent(event); + return element.dispatchEvent(event); } else if (typeof element.fireEvent === 'function') { event = $.extend(document.createEventObject(), opts); - element.fireEvent('on' + type, event); + return element.fireEvent('on' + type, event); } else if (typeof element[type]) { element[type](); + return true; + } + }; + + /** + * Strips HTML and returns just text. + * + * @param {String|Element|jQuery} html + * A string of HTML content, an Element DOM object or a jQuery object. + * + * @return {String} + * The text without HTML tags. + * + * @todo Replace with http://locutus.io/php/strings/strip_tags/ + */ + Bootstrap.stripHtml = function (html) { + if (html instanceof $) { + html = html.html(); } + else if (html instanceof Element) { + html = html.innerHTML; + } + var tmp = document.createElement('DIV'); + tmp.innerHTML = html; + return (tmp.textContent || tmp.innerText || '').replace(/^[\s\n\t]*|[\s\n\t]*$/g, ''); }; /** @@ -425,12 +475,24 @@ * The value of the unsupported object. */ Bootstrap.unsupported = function (type, name, value) { + Bootstrap.warn('Unsupported by Drupal Bootstrap: (@type) @name -> @value', { + '@type': type, + '@name': name, + '@value': typeof value === 'object' ? JSON.stringify(value) : value + }); + }; + + /** + * Provide a helper method to display a warning. + * + * @param {String} message + * The message to display. + * @param {Object} [args] + * Arguments to use as replacements in Drupal.formatString. + */ + Bootstrap.warn = function (message, args) { if (this.settings.dev && console.warn) { - console.warn(Drupal.formatString('Unsupported Drupal Bootstrap Modal @type: @name -> @value', { - '@type': type, - '@name': name, - '@value': typeof value === 'object' ? JSON.stringify(value) : value - })); + console.warn(Drupal.formatString(message, args)); } }; diff --git a/web/themes/contrib/bootstrap/js/misc/dialog.ajax.js b/web/themes/contrib/bootstrap/js/misc/dialog.ajax.js index f5c853cb4..7bfdcd33a 100644 --- a/web/themes/contrib/bootstrap/js/misc/dialog.ajax.js +++ b/web/themes/contrib/bootstrap/js/misc/dialog.ajax.js @@ -2,25 +2,34 @@ * @file * dialog.ajax.js */ -(function ($, Drupal) { +(function ($, Drupal, Bootstrap) { - var dialogAjaxCurrentButton; - var dialogAjaxOriginalButton; + Drupal.behaviors.dialog.ajaxCurrentButton = null; + Drupal.behaviors.dialog.ajaxOriginalButton = null; + + /** + * Synchronizes a faux button with its original counterpart. + * + * @param {Boolean} [reset = false] + * Whether to reset the current and original buttons after synchronizing. + */ + Drupal.behaviors.dialog.ajaxUpdateButtons = function (reset) { + if (this.ajaxCurrentButton && this.ajaxOriginalButton) { + this.ajaxCurrentButton.html(this.ajaxOriginalButton.html()); + this.ajaxCurrentButton.prop('disabled', this.ajaxOriginalButton.prop('disabled')); + } + if (reset) { + this.ajaxCurrentButton = null; + this.ajaxOriginalButton = null; + } + }; $(document) .ajaxSend(function () { - if (dialogAjaxCurrentButton && dialogAjaxOriginalButton) { - dialogAjaxCurrentButton.html(dialogAjaxOriginalButton.html()); - dialogAjaxCurrentButton.prop('disabled', dialogAjaxOriginalButton.prop('disabled')); - } + Drupal.behaviors.dialog.ajaxUpdateButtons(); }) .ajaxComplete(function () { - if (dialogAjaxCurrentButton && dialogAjaxOriginalButton) { - dialogAjaxCurrentButton.html(dialogAjaxOriginalButton.html()); - dialogAjaxCurrentButton.prop('disabled', dialogAjaxOriginalButton.prop('disabled')); - } - dialogAjaxCurrentButton = null; - dialogAjaxOriginalButton = null; + Drupal.behaviors.dialog.ajaxUpdateButtons(true); }) ; @@ -28,34 +37,46 @@ * {@inheritdoc} */ Drupal.behaviors.dialog.prepareDialogButtons = function prepareDialogButtons($dialog) { + var _that = this; var buttons = []; var $buttons = $dialog.find('.form-actions').find('button, input[type=submit], .form-actions a.button'); $buttons.each(function () { - var $originalButton = $(this).css({ - display: 'block', - width: 0, - height: 0, - padding: 0, - border: 0, - overflow: 'hidden' - }); - var classes = $originalButton.attr('class').replace('use-ajax-submit', ''); + var $originalButton = $(this) + // Prevent original button from being tabbed to. + .attr('tabindex', -1) + // Visually make the original button invisible, but don't actually hide + // or remove it from the DOM because the click needs to be proxied from + // the faux button created in the footer to its original counterpart. + .css({ + display: 'block', + width: 0, + height: 0, + padding: 0, + border: 0, + overflow: 'hidden' + }); + buttons.push({ - text: $originalButton.html() || $originalButton.attr('value'), - class: classes, + // Strip all HTML from the actual text value. This value is escaped. + // It actual HTML value will be synced with the original button's HTML + // below in the "create" method. + text: Bootstrap.stripHtml($originalButton), + class: $originalButton.attr('class').replace('use-ajax-submit', ''), click: function click(e) { - dialogAjaxCurrentButton = $(e.target); - dialogAjaxOriginalButton = $originalButton; - if ($originalButton.is('a')) { - $originalButton[0].click(); - } - else { - $originalButton.trigger('mousedown').trigger('mouseup').trigger('click'); - e.preventDefault(); - } + e.preventDefault(); + e.stopPropagation(); + _that.ajaxCurrentButton = $(e.target); + _that.ajaxOriginalButton = $originalButton; + Bootstrap.simulate($originalButton, 'click'); + }, + create: function () { + _that.ajaxCurrentButton = $(this); + _that.ajaxOriginalButton = $originalButton; + _that.ajaxUpdateButtons(true); } }); }); + return buttons; }; diff --git a/web/themes/contrib/bootstrap/js/misc/vertical-tabs.js b/web/themes/contrib/bootstrap/js/misc/vertical-tabs.js index 0085c4db7..83cfec446 100644 --- a/web/themes/contrib/bootstrap/js/misc/vertical-tabs.js +++ b/web/themes/contrib/bootstrap/js/misc/vertical-tabs.js @@ -6,6 +6,23 @@ (function ($, window, Drupal, drupalSettings) { "use strict"; + /** + * Show the parent vertical tab pane of a targeted page fragment. + * + * In order to make sure a targeted element inside a vertical tab pane is + * visible on a hash change or fragment link click, show all parent panes. + * + * @param {jQuery.Event} e + * The event triggered. + * @param {jQuery} $target + * The targeted node as a jQuery object. + */ + var handleFragmentLinkClickOrHashChange = function handleFragmentLinkClickOrHashChange(e, $target) { + $target.parents('.vertical-tabs-pane').each(function (index, pane) { + $(pane).data('verticalTab').focus(); + }); + }; + /** * This script transforms a set of details into a stack of vertical * tabs. Another tab pane can be selected by clicking on the respective @@ -26,6 +43,11 @@ return; } + /** + * Binds a listener to handle fragment link clicks and URL hash changes. + */ + $('body').once('vertical-tabs-fragments').on('formFragmentLinkClickOrHashChange.verticalTabs', handleFragmentLinkClickOrHashChange); + $(context).find('[data-vertical-tabs-panes]').once('vertical-tabs').each(function () { var $this = $(this).addClass('tab-content vertical-tabs-panes'); diff --git a/web/themes/contrib/bootstrap/js/modal.jquery.ui.bridge.js b/web/themes/contrib/bootstrap/js/modal.jquery.ui.bridge.js index 88d5ce480..cd9509251 100644 --- a/web/themes/contrib/bootstrap/js/modal.jquery.ui.bridge.js +++ b/web/themes/contrib/bootstrap/js/modal.jquery.ui.bridge.js @@ -127,18 +127,58 @@ */ createButtons: function () { this.$footer.find('.modal-buttons').remove(); + + // jQuery UI supports both objects and arrays. Unfortunately + // developers have misunderstood and abused this by simply placing + // the objects that should be in an array inside an object with + // arbitrary keys (likely to target specific buttons as a hack). var buttons = this.options.dialogOptions && this.options.dialogOptions.buttons || []; + if (!Array.isArray(buttons)) { + var array = []; + for (var k in buttons) { + // Support the proper object values: label => click callback. + if (typeof buttons[k] === 'function') { + array.push({ + label: k, + click: buttons[k], + }); + } + // Support nested objects, but log a warning. + else if (buttons[k].text || buttons[k].label) { + Bootstrap.warn('Malformed jQuery UI dialog button: @key. The button object should be inside an array.', { + '@key': k + }); + array.push(buttons[k]); + } + else { + Bootstrap.unsupported('button', k, buttons[k]); + } + } + buttons = array; + } + if (buttons.length) { var $buttons = $('