From b34aaca2e848e87e1a91106a9395017a423c08c2 Mon Sep 17 00:00:00 2001 From: Mateusz Uzdowski Date: Mon, 14 Jul 2014 11:15:38 +1200 Subject: [PATCH] BUG Fix several issues around onmatch/onunmatch entwines. 1. Add missing _super calls. 2. Make UI widget destroys more consistent to avoid exceptions. Selectable would throw an exception in the GridField.js if destroy called from onunmatch - at that stage jQuery UI would have had called the destroy already. Add a guard, and change to onremove, which triggers before the element is removed from DOM. 3. DOM traversal fails after the element is removed from DOM. Onunmatch triggers after the removal of the element from the DOM, which makes DOM traversal fail. Use onremove instead, which triggers while the element is still in DOM. --- admin/javascript/LeftAndMain.FieldHelp.js | 6 +++-- admin/javascript/LeftAndMain.Preview.js | 3 ++- admin/javascript/LeftAndMain.js | 4 ++-- .../MemberDatetimeOptionsetField.js | 2 +- javascript/GridField.js | 24 ++++++++++--------- javascript/HtmlEditorField.js | 1 + javascript/TabSet.js | 4 ++-- javascript/ToggleCompositeField.js | 7 +++--- javascript/TreeDropdownField.js | 2 ++ javascript/UploadField.js | 24 ++++++++++++------- 10 files changed, 46 insertions(+), 31 deletions(-) diff --git a/admin/javascript/LeftAndMain.FieldHelp.js b/admin/javascript/LeftAndMain.FieldHelp.js index 60ada309746..c94394009f2 100644 --- a/admin/javascript/LeftAndMain.FieldHelp.js +++ b/admin/javascript/LeftAndMain.FieldHelp.js @@ -11,6 +11,8 @@ */ $(".cms .field.cms-description-tooltip").entwine({ onmatch: function() { + this._super(); + var descriptionEl = this.find('.description'), inputEl, tooltipEl; if(descriptionEl.length) { this @@ -19,8 +21,8 @@ .tooltip({content: descriptionEl.html()}); descriptionEl.remove(); } - } - }); + }, + }); $(".cms .field.cms-description-tooltip :input").entwine({ onfocusin: function(e) { diff --git a/admin/javascript/LeftAndMain.Preview.js b/admin/javascript/LeftAndMain.Preview.js index 3a0f765946f..7e8ff317d88 100644 --- a/admin/javascript/LeftAndMain.Preview.js +++ b/admin/javascript/LeftAndMain.Preview.js @@ -517,7 +517,8 @@ }); $('.cms-edit-form').entwine({ - onadd: function() { + onadd: function() { + this._super(); $('.cms-preview')._initialiseFromContent(); } }); diff --git a/admin/javascript/LeftAndMain.js b/admin/javascript/LeftAndMain.js index 680155acc25..4650f1d9623 100644 --- a/admin/javascript/LeftAndMain.js +++ b/admin/javascript/LeftAndMain.js @@ -948,8 +948,8 @@ jQuery.noConflict(); setTimeout(function() { form.clickedButton = null; }, 10); - } - }); + } + }); this.redraw(); this._super(); diff --git a/admin/javascript/MemberDatetimeOptionsetField.js b/admin/javascript/MemberDatetimeOptionsetField.js index b35cc0d4f06..e2f86ab509e 100644 --- a/admin/javascript/MemberDatetimeOptionsetField.js +++ b/admin/javascript/MemberDatetimeOptionsetField.js @@ -1,6 +1,6 @@ (function($) { $.entwine('ss', function($){ - + $('.memberdatetimeoptionset').entwine({ onmatch: function() { this.find('.description .toggle-content').hide(); diff --git a/javascript/GridField.js b/javascript/GridField.js index 86fa7026023..20981a2352b 100644 --- a/javascript/GridField.js +++ b/javascript/GridField.js @@ -8,7 +8,7 @@ */ reload: function(ajaxOpts, successCallback) { - var self = this, form = this.closest('form'), + var self = this, form = this.closest('form'), focusedElName = this.find(':input:focus').attr('name'), // Save focused element for restoring after refresh data = form.find(':input').serializeArray(); @@ -23,7 +23,7 @@ ajaxOpts.data = window.location.search.replace(/^\?/, '') + '&' + $.param(ajaxOpts.data); } - // For browsers which do not support history.pushState like IE9, ss framework uses hash to track + // For browsers which do not support history.pushState like IE9, ss framework uses hash to track // the current location for PJAX, so for them we pass the query string stored in the hash instead if(!window.history || !window.history.pushState){ if(window.location.hash && window.location.hash.indexOf('?') != -1){ @@ -48,15 +48,15 @@ // multiple relationships via keyboard. if(focusedElName) self.find(':input[name="' + focusedElName + '"]').focus(); - // Update filter + // Update filter if(self.find('.filter-header').length) { var content; if(ajaxOpts.data[0].filter=="show") { content = ''; - self.addClass('show-filter').find('.filter-header').show(); + self.addClass('show-filter').find('.filter-header').show(); } else { content = ''; - self.removeClass('show-filter').find('.filter-header').hide(); + self.removeClass('show-filter').find('.filter-header').hide(); } self.find('.sortable-header th:last').html(content); @@ -104,7 +104,7 @@ $('.ss-gridfield :button[name=showFilter]').entwine({ - onclick: function(e) { + onclick: function(e) { $('.filter-header') .show('slow') // animate visibility .find(':input:first').focus(); // focus first search field @@ -198,11 +198,13 @@ $('.ss-gridfield-print-iframe').entwine({ onmatch: function(){ + this._super(); + this.hide().bind('load', function() { this.focus(); var ifWin = this.contentWindow || this; ifWin.print(); - });; + }); }, onunmatch: function() { this._super(); @@ -268,15 +270,15 @@ } }); $('.ss-gridfield[data-selectable] .ss-gridfield-items').entwine({ - onmatch: function() { + onadd: function() { this._super(); - + // TODO Limit to single selection this.selectable(); }, - onunmatch: function() { + onremove: function() { this._super(); - this.selectable('destroy'); + if (this.data('selectable')) this.selectable('destroy'); } }); diff --git a/javascript/HtmlEditorField.js b/javascript/HtmlEditorField.js index d0b34b30e25..93c91836697 100644 --- a/javascript/HtmlEditorField.js +++ b/javascript/HtmlEditorField.js @@ -959,6 +959,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE; $('form.htmleditorfield-form.htmleditorfield-mediaform input.remoteurl').entwine({ onadd: function() { + this._super(); this.validate(); }, diff --git a/javascript/TabSet.js b/javascript/TabSet.js index 44d5203aa49..e61412af25d 100644 --- a/javascript/TabSet.js +++ b/javascript/TabSet.js @@ -12,7 +12,7 @@ this._super(); }, onremove: function() { - if(this.data('uiTabs')) this.tabs('destroy'); + if(this.data('tabs')) this.tabs('destroy'); this._super(); }, redrawTabs: function() { @@ -32,7 +32,7 @@ if(!matches) return; $(this).attr('href', document.location.href.replace(/#.*/, '') + matches[0]); }); - } + } }); }); })(jQuery); diff --git a/javascript/ToggleCompositeField.js b/javascript/ToggleCompositeField.js index ac891361818..75d8b8c37ae 100644 --- a/javascript/ToggleCompositeField.js +++ b/javascript/ToggleCompositeField.js @@ -2,15 +2,16 @@ $.entwine('ss', function($){ $('.ss-toggle').entwine({ onadd: function() { + this._super(); + this.accordion({ collapsible: true, active: (this.hasClass("ss-toggle-start-closed")) ? false : 0 }); - - this._super(); }, onremove: function() { - this.accordion('destroy'); + if (this.data('accordion')) this.accordion('destroy'); + this._super(); }, getTabSet: function() { diff --git a/javascript/TreeDropdownField.js b/javascript/TreeDropdownField.js index 29dbf756dcc..b6faeb8236d 100644 --- a/javascript/TreeDropdownField.js +++ b/javascript/TreeDropdownField.js @@ -423,11 +423,13 @@ $('.TreeDropdownField input[type=hidden]').entwine({ onadd: function() { + this._super(); this.bind('change.TreeDropdownField', function() { $(this).getField().updateTitle(); }); }, onremove: function() { + this._super(); this.unbind('.TreeDropdownField'); } }); diff --git a/javascript/UploadField.js b/javascript/UploadField.js index 5f791306cbc..fd83b0b9514 100644 --- a/javascript/UploadField.js +++ b/javascript/UploadField.js @@ -328,11 +328,11 @@ } }); $('div.ss-upload .ss-uploadfield-files .ss-uploadfield-item').entwine({ - onmatch: function() { + onadd: function() { this._super(); this.closest('.ss-upload').find('.ss-uploadfield-addfile').addClass('borderTop'); }, - onunmatch: function() { + onremove: function() { $('.ss-uploadfield-files:not(:has(.ss-uploadfield-item))').closest('.ss-upload').find('.ss-uploadfield-addfile').removeClass('borderTop'); this._super(); } @@ -365,19 +365,25 @@ if(config.changeDetection) { this.closest('form').trigger('dirty'); } - fileupload._trigger('destroy', e, { - context: item, - url: this.data('href'), - type: 'get', - dataType: fileupload.options.dataType - }); + + if (fileupload) { + fileupload._trigger('destroy', e, { + context: item, + url: this.data('href'), + type: 'get', + dataType: fileupload.options.dataType + }); + } } } else { // Removed files will be applied to object on save if(config.changeDetection) { this.closest('form').trigger('dirty'); } - fileupload._trigger('destroy', e, {context: item}); + + if (fileupload) { + fileupload._trigger('destroy', e, {context: item}); + } } e.preventDefault(); // Avoid a form submit