summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Carver2018-12-21 05:53:37 (GMT)
committerMark Carver2018-12-21 05:53:37 (GMT)
commit26c83e1eab77e0440e351bf8be23085401925d9b (patch)
treeaee9a17781a97858177d0ca34b294c81a36a049a
parentf240b0d5c3bd01be41effef6dc120a92303b4074 (diff)
Issue #2997347 by markcarver, imclean, pwaterz: Using $.fn.dialog duplicates content when it lives in DOM
-rw-r--r--js/modal.jquery.ui.bridge.js95
-rw-r--r--js/modal.js7
2 files changed, 56 insertions, 46 deletions
diff --git a/js/modal.jquery.ui.bridge.js b/js/modal.jquery.ui.bridge.js
index d7f08e2..53a5484 100644
--- a/js/modal.jquery.ui.bridge.js
+++ b/js/modal.jquery.ui.bridge.js
@@ -38,58 +38,69 @@
/**
* Proxy $.fn.dialog to $.fn.modal.
*/
- Bootstrap.createPlugin('dialog', function (options) {
+ var Dialog = function (options) {
// When only options are passed, jQuery UI dialog treats this like a
// initialization method. Destroy any existing Bootstrap modal and
// recreate it using the contents of the dialog HTML.
if (arguments.length === 1 && typeof options === 'object') {
- this.each(function () {
- // This part gets a little tricky. Core can potentially already
- // semi-process this "dialog" if was created using an Ajax command
- // (i.e. prepareDialogButtons in drupal.ajax.js). Because of this,
- // we cannot simply dump the existing dialog content into a newly
- // created modal because that would destroy any existing event
- // bindings. Instead, we have to create this in steps and "move"
- // (append) the existing content as needed.
- var $this = $(this);
-
- // Create a new modal to get a complete template.
- var $modal = $(Drupal.theme('bootstrapModal', {attributes: Attributes.create(this).remove('style')}));
-
- // Store a reference to the content inside the existing dialog.
- // This references the actual DOM node elements which will allow
- // jQuery to "move" then when appending below. Using $.fn.children()
- // does not return any text nodes present and $.fn.html() only returns
- // a string representation of the content, which effectively destroys
- // any prior event bindings or processing.
- var $existing = $this.contents();
-
- // Destroy any existing Bootstrap Modal data that may have been saved.
- $this.removeData('bs.modal');
-
- // Set the attributes of the dialog to that of the newly created modal.
- $this.attr(Attributes.create($modal).toPlainObject());
-
- // Append the newly created modal markup.
- $this.append($modal.html());
-
- // Move the existing HTML into the modal markup that was just appended.
- $this.find('.modal-body').append($existing);
- });
-
- // Indicate that the modal is a jQuery UI dialog bridge.
- options = {
+ this.each($.fn.dialog.ensureModalStructure);
+
+ // Proxy to the Bootstrap Modal plugin, indicating that this is a
+ // jQuery UI dialog bridge.
+ return $.fn.modal.apply(this, [{
dialogOptions: options,
jQueryUiBridge: true
- };
-
- // Proxy just the options to the Bootstrap Modal plugin.
- return $.fn.modal.apply(this, [options]);
+ }]);
}
// Otherwise, proxy all arguments to the Bootstrap Modal plugin.
return $.fn.modal.apply(this, arguments);
- });
+ };
+
+ /**
+ * Ensures a DOM element has the appropriate structure for a modal.
+ *
+ * Note: this can get a little tricky. Core potentially already
+ * semi-processes a "dialog" if was created using an Ajax command
+ * (i.e. prepareDialogButtons in drupal.ajax.js). Because of this, the
+ * contents (HTML) of the existing element cannot simply be dumped into a
+ * newly created modal. This would destroy any existing event bindings.
+ * Instead, the contents must be "moved" (appended) to the new modal and
+ * then "moved" again back to the to the existing container as needed.
+ */
+ Dialog.ensureModalStructure = function () {
+ var $element = $(this);
+
+ // Immediately return if the modal was already converted into a proper modal.
+ if ($element.is('[data-drupal-theme="bootstrapModal"]')) {
+ return;
+ }
+
+ // Create a new modal.
+ var $modal = $(Drupal.theme('bootstrapModal', {
+ attributes: Attributes.create(this).remove('style').set('data-drupal-theme', 'bootstrapModal'),
+ }));
+
+ // Store a reference to the content inside the existing element container.
+ // This references the actual DOM node elements which will allow
+ // jQuery to "move" then when appending below. Using $.fn.children()
+ // does not return any text nodes present and $.fn.html() only returns
+ // a string representation of the content, which effectively destroys
+ // any prior event bindings or processing.
+ var $body = $element.find('.modal-body');
+ var $existing = $body[0] ? $body.contents() : $element.contents();
+
+ // Set the attributes of the dialog to that of the newly created modal.
+ $element.attr(Attributes.create($modal).toPlainObject());
+
+ // Append the newly created modal markup.
+ $element.append($modal.html());
+
+ // Move the existing HTML into the modal markup that was just appended.
+ $element.find('.modal-body').append($existing);
+ };
+
+ Bootstrap.createPlugin('dialog', Dialog);
/**
* Extend the Bootstrap Modal plugin constructor class.
diff --git a/js/modal.js b/js/modal.js
index 151bc19..58be56f 100644
--- a/js/modal.js
+++ b/js/modal.js
@@ -155,6 +155,7 @@
}
options = Bootstrap.normalizeObject($.extend({}, Modal.DEFAULTS, data && data.options, $this.data(), options));
+ delete options['bs.modal'];
if (!data) {
$this.data('bs.modal', (data = new Modal(this, options)));
@@ -180,12 +181,10 @@
Bootstrap.unsupported('method', method);
}
}
+ // No method, set options and open if necessary.
else {
- // If no method set the options.
data.option(options);
-
- // Handle native modal showing.
- if (!options.jQueryUiBridge && options.show && !data.isShown) {
+ if (options.show && !data.isShown) {
data.show(relatedTarget);
}
}