diff --git a/indigo_app/static/javascript/indigo/models.js b/indigo_app/static/javascript/indigo/models.js index 7c5326a5e..8bf1b455b 100644 --- a/indigo_app/static/javascript/indigo/models.js +++ b/indigo_app/static/javascript/indigo/models.js @@ -24,7 +24,7 @@ * This model fires custom events: * * - mutation - when the XML DOM is manipulated by any means, based on the MutationObserver class. The parameter - * for the event is a MutationRecord object. + * for the event is list of MutationRecord objects. * - change:dom - when the XML DOM is manipulated by any means, after all the mutation events have been fired. */ Indigo.DocumentContent = Backbone.Model.extend({ @@ -47,10 +47,8 @@ setupMutationObserver: function () { this.observer = new MutationObserver((mutations) => { - for (const mutation of mutations) { - console.log('mutation', mutation); - this.trigger('mutation', this, mutation); - } + console.log('mutations', mutations); + this.trigger('mutation', this, mutations); this.trigger('change:dom', this); this.trigger('change', this); }); diff --git a/indigo_app/static/javascript/indigo/views/document_editor.js b/indigo_app/static/javascript/indigo/views/document_editor.js index b20ceb03a..6b728ae3d 100644 --- a/indigo_app/static/javascript/indigo/views/document_editor.js +++ b/indigo_app/static/javascript/indigo/views/document_editor.js @@ -189,49 +189,52 @@ * The XML document has changed, re-render if it impacts our xmlElement. * * @param model documentContent model - * @param mutation a MutationRecord object + * @param mutations an array of MutationRecord objects */ - onDomMutated: function(model, mutation) { - let eid = mutation.target.getAttribute ? mutation.target.getAttribute('eId') : null; - - switch (model.getMutationImpact(mutation, this.xmlElement)) { - case 'replaced': - this.xmlElement = mutation.addedNodes[0]; - this.render(); - break; - case 'changed': - if (this.quickEditEid && eid !== this.quickEditEid) { - // Check for the special case of the quick-edited element being replaced, so that we can re-render - // just that element (if possible). In this case the mutation target is the parent and its children have - // changed. - - if (mutation.type === 'childList' && mutation.removedNodes.length === 1 && - mutation.removedNodes[0]?.getAttribute('eId') === this.quickEditEid) { - // the quick-edited element was removed or replaced - - if (mutation.addedNodes.length === 1) { - if (mutation.addedNodes[0]?.getAttribute('eId') === this.quickEditEid) { - // it was replaced but retained the eid - eid = this.quickEditEid; + onDomMutated: function(model, mutations) { + // process each mutation in order; we stop processing after finding the first one that significantly impacts us + for (const mutation of mutations) { + let eid = mutation.target.getAttribute ? mutation.target.getAttribute('eId') : null; + + switch (model.getMutationImpact(mutation, this.xmlElement)) { + case 'replaced': + this.xmlElement = mutation.addedNodes[0]; + this.render(); + return; + case 'changed': + if (this.quickEditEid && eid !== this.quickEditEid) { + // Check for the special case of the quick-edited element being replaced, so that we can re-render + // just that element (if possible). In this case the mutation target is the parent and its children have + // changed. + + if (mutation.type === 'childList' && mutation.removedNodes.length === 1 && + mutation.removedNodes[0]?.getAttribute('eId') === this.quickEditEid) { + // the quick-edited element was removed or replaced + + if (mutation.addedNodes.length === 1) { + if (mutation.addedNodes[0]?.getAttribute('eId') === this.quickEditEid) { + // it was replaced but retained the eid + eid = this.quickEditEid; + } else { + // it was replaced with a different eid; re-render the target but track the new eid + this.quickEditEid = mutation.addedNodes[0].getAttribute('eId'); + } } else { - // it was replaced with a different eid; re-render the target but track the new eid - this.quickEditEid = mutation.addedNodes[0].getAttribute('eId'); + // it was removed + this.quickEditEid = null; } - } else { - // it was removed - this.quickEditEid = null; } } - } - // eid can be null, in which case everything is re-rendered - this.render(eid); - break; - case 'removed': - // the change removed xmlElement from the tree - console.log('Mutation removes SourceEditor.xmlElement from the tree'); - this.discardChanges(); - break; + // eid can be null, in which case everything is re-rendered + this.render(eid); + return; + case 'removed': + // the change removed xmlElement from the tree + console.log('Mutation removes SourceEditor.xmlElement from the tree'); + this.discardChanges(); + return; + } } }, diff --git a/indigo_app/static/javascript/indigo/views/document_xml_editor.js b/indigo_app/static/javascript/indigo/views/document_xml_editor.js index 9a422adb4..e132b0b11 100644 --- a/indigo_app/static/javascript/indigo/views/document_xml_editor.js +++ b/indigo_app/static/javascript/indigo/views/document_xml_editor.js @@ -272,28 +272,31 @@ class AknTextEditor { * The XML document has changed, re-render if it impacts our xmlElement. * * @param model documentContent model - * @param mutation a MutationRecord object + * @param mutations an array of MutationRecord objects */ - onDocumentMutated (model, mutation) { + onDocumentMutated (model, mutations) { if (!this.editing) return; - switch (model.getMutationImpact(mutation, this.xmlElement)) { - case 'replaced': - this.xmlElement = mutation.addedNodes[0]; - // fall through to 'changed' - case 'changed': - if (!this.updating) { - // the XML has changed, update the text in the editor - this.previousText = this.unparse(); - const posn = this.monacoEditor.getPosition(); - this.monacoEditor.setValue(this.previousText); - this.monacoEditor.setPosition(posn); - } - break; - case 'removed': - console.log('Mutation removes AknTextEditor.xmlElement from the tree'); - this.discardChanges(); - break; + // process each mutation in order; we stop processing after finding the first one that significantly impacts us + for (const mutation of mutations) { + switch (model.getMutationImpact(mutation, this.xmlElement)) { + case 'replaced': + this.xmlElement = mutation.addedNodes[0]; + // fall through to 'changed' + case 'changed': + if (!this.updating) { + // the XML has changed, update the text in the editor + this.previousText = this.unparse(); + const posn = this.monacoEditor.getPosition(); + this.monacoEditor.setValue(this.previousText); + this.monacoEditor.setPosition(posn); + } + return; + case 'removed': + console.log('Mutation removes AknTextEditor.xmlElement from the tree'); + this.discardChanges(); + return; + } } } diff --git a/indigo_app/static/javascript/indigo/views/table_editor.js b/indigo_app/static/javascript/indigo/views/table_editor.js index 44dca3487..9d52215da 100644 --- a/indigo_app/static/javascript/indigo/views/table_editor.js +++ b/indigo_app/static/javascript/indigo/views/table_editor.js @@ -93,23 +93,26 @@ * The XML document has changed, re-render if it impacts our table element. * * @param model documentContent model - * @param mutation a MutationRecord object + * @param mutations an array of MutationRecord objects */ - onDomMutated (model, mutation) { + onDomMutated (model, mutations) { if (!this.editing) return; - switch (model.getMutationImpact(mutation, this.table)) { - case 'replaced': - this.discardChanges(true); - break; - case 'changed': - this.discardChanges(true); - break; - case 'removed': - // the change removed xmlElement from the tree - console.log('Mutation removes TableEditor.table from the tree'); - this.discardChanges(true); - break; + // process each mutation in order; we stop processing after finding the first one that significantly impacts us + for (const mutation of mutations) { + switch (model.getMutationImpact(mutation, this.table)) { + case 'replaced': + this.discardChanges(true); + return; + case 'changed': + this.discardChanges(true); + return; + case 'removed': + // the change removed xmlElement from the tree + console.log('Mutation removes TableEditor.table from the tree'); + this.discardChanges(true); + return; + } } },