diff --git a/panels_ipe/js/panels_ipe.js b/panels_ipe/js/panels_ipe.js index b556e43..4c4fec5 100644 --- a/panels_ipe/js/panels_ipe.js +++ b/panels_ipe/js/panels_ipe.js @@ -65,6 +65,10 @@ function DrupalPanelsIPE(cache_key, cfg) { this.container = $('#panels-ipe-control-container'); this.control = $('div#panels-ipe-control-' + cache_key); this.initButton = $('div.panels-ipe-startedit', this.control); + this.topParent = $('div#panels-ipe-display-' + cache_key); + // Make a backup of the display (and all its attached event handlers) for use + // in the cancelIPE() method. + this.backup = this.topParent.clone(true); this.cfg = cfg; this.changed = false; this.sortableOptions = $.extend({ @@ -188,13 +192,10 @@ function DrupalPanelsIPE(cache_key, cfg) { this.initEditing = function(formdata) { ipe.editing = true; - ipe.topParent = $('div#panels-ipe-display-' + cache_key); - ipe.backup = this.topParent.clone(); + ipe.changed = false; // See http://jqueryui.com/demos/sortable/ for details on the configuration // parameters used here. - ipe.changed = false; - $('div.panels-ipe-sort-container', ipe.topParent).each(ipe.initSorting); // Since the connectWith option only does a one-way hookup, iterate over @@ -287,16 +288,36 @@ function DrupalPanelsIPE(cache_key, cfg) { this.cancelIPE = function() { ipe.hideContainer(); ipe.topParent.fadeOut('medium', function() { - ipe.topParent.replaceWith(ipe.backup.clone()); + // Replace the pane display with the original, unchanged version. Pass + // "true" to the clone() method to ensure that we get any event handlers + // that were attached to the backup. (Since the Panels IPE JavaScript is + // added to the page with a very low weight, we expect its behaviors to + // run first, so there shouldn't have been any event handlers that were + // added by standard Drupal behaviors before the backup was originally + // made. However, it's still a good idea to pass "true" so that we get + // any event handlers added by code that ran outside the Drupal behaviors + // system.) + ipe.topParent.replaceWith(ipe.backup.clone(true)); + + // Reset the ipe.topParent variable, and display its content. ipe.topParent = $('div#panels-ipe-display-' + ipe.key); - - // Processing of these things got lost in the cloning, but the classes remained behind. - // @todo this isn't ideal but I can't seem to figure out how to keep an unprocessed backup - // that will later get processed. - $('.ctools-use-modal-processed', ipe.topParent).removeClass('ctools-use-modal-processed'); - $('.pane-delete-processed', ipe.topParent).removeClass('pane-delete-processed'); ipe.topParent.fadeIn('medium'); + + // As described above, the backup isn't expected to have many (or any) + // event handlers attached to it since it was originally made before + // other JavaScript behaviors had a chance to run. So, re-attach + // behaviors now to get the full set of event handlers. Note that the + // reason we do it this way (rather than the opposite; i.e., rather than + // having the Panels IPE behaviors run last so that the backup is made + // after all other behaviors have run and all event handlers have already + // been attached) is that many event handlers maintain a global state + // which contains references to the original object and therefore won't + // work correctly on the cloned object. Drupal.attachBehaviors(); + + // Re-attaching behaviors won't cause this object to be initialized + // again, though, so we need to re-initialize it manually. + ipe.init(); }); }; @@ -326,10 +347,17 @@ function DrupalPanelsIPE(cache_key, cfg) { $(this).prependTo($(this).parent().parent()); }); }); - } + }; - this.createSortContainers(); + /** + * Triggers methods that must run when this object is initialized. + */ + this.init = function () { + this.createSortContainers(); + } + // Initialize the object. + this.init(); }; $(function() { diff --git a/panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php b/panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php index 0d7198f..57448ff 100644 --- a/panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php +++ b/panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php @@ -105,9 +105,12 @@ class panels_renderer_ipe extends panels_renderer_editor { ctools_add_css('panels_dnd', 'panels'); ctools_add_css('panels_admin', 'panels'); ctools_add_js('panels-base', 'panels'); - ctools_add_js('panels_ipe', 'panels_ipe'); ctools_add_css('panels_ipe', 'panels_ipe'); + // Add the JavaScript for the IPE. This must go as early as possible so + // that its behaviors run first, allowing it to clone parts of the DOM + // before other behaviors have been applied to them. + drupal_add_js(drupal_get_path('module', 'panels_ipe') . '/js/panels_ipe.js', array('group' => JS_LIBRARY, 'weight' => -1000)); drupal_add_js(array('PanelsIPECacheKeys' => array($this->clean_key)), 'setting'); drupal_add_library('system', 'ui.draggable');