X-Git-Url: https://git.mdrn.pl/fnpeditor.git/blobdiff_plain/471fbe0d1628d3f2baabd39e02a4e58f7d9bca5c..c11ad12846df3142b19a343144fe1d2a82b97569:/src/smartxml/core.js diff --git a/src/smartxml/core.js b/src/smartxml/core.js index 17af8ac..9630a77 100644 --- a/src/smartxml/core.js +++ b/src/smartxml/core.js @@ -13,7 +13,7 @@ var INSERTION = function(implementation) { nodeParent, returned; options = options || {}; - if(!(this.document.containsNode(this))) { + if(!(this.document.containsNode(this)) || !insertion.isNew) { nodeParent = insertion.ofNode.parent(); } returned = implementation.call(this, insertion.ofNode); @@ -27,9 +27,16 @@ var INSERTION = function(implementation) { var documentNodeTransformations = { detach: function() { - var parent = this.parent(); + var parent = this.parent(), + existed = this.document.containsNode(this); this._$.detach(); - this.triggerChangeEvent('nodeDetached', {parent: parent}); + if(existed) { + this.triggerChangeEvent('nodeDetached', {parent: parent}); + if(!parent) { + // This was the root of the document + this.document._defineDocumentProperties(null); + } + } return this; }, @@ -38,15 +45,19 @@ var documentNodeTransformations = { if(this.isRoot()) { return this.document.replaceRoot(node); } - toret = this.after(node); - this.detach(); - return toret; + if(this.parent()) { + toret = this.after(node); + this.detach(); + return toret; + } + throw new Error('Cannot replace node without a parent.'); }, after: INSERTION(function(node) { var next = this.next(); if(next && next.nodeType === Node.TEXT_NODE && node.nodeType === Node.TEXT_NODE) { next.setText(node.getText() + next.getText()); + node.detach(); return next; } this._$.after(node.nativeNode); @@ -57,6 +68,7 @@ var documentNodeTransformations = { var prev = this.prev(); if(prev && prev.nodeType === Node.TEXT_NODE && node.nodeType === Node.TEXT_NODE) { prev.setText(prev.getText() + node.getText()); + node.detach(); return prev; } this._$.before(node.nativeNode); @@ -65,8 +77,9 @@ var documentNodeTransformations = { wrapWith: function(node) { var insertion = this.getNodeInsertion(node); - if(this.parent()) { - this.before(insertion.ofNode); + + if(this.parent() || this.isRoot()) { + this.replaceWith(insertion.ofNode); } insertion.ofNode.append(this); return insertion.ofNode; @@ -102,26 +115,21 @@ var elementNodeTransformations = { }, setTag: function(tagName) { - var node = this.document.createDocumentNode({tagName: tagName}), - oldTagName = this.getTagName(), - myContents = this._$.contents(); + var node = this.document.createDocumentNode({tagName: tagName}); this.getAttrs().forEach(function(attribute) { - node.setAttr(attribute.name, attribute.value, true); + node.setAttr(attribute.name, attribute.value); }); - node.setData(this.getData()); - if(this.sameNode(this.document.root)) { - this.document._defineDocumentProperties(node._$); - } + this.contents().forEach(function(child) { + node.append(child); + }); - /* TODO: This invalidates old references to this node. Caching instances on nodes would fix this. */ - this._$.replaceWith(node._$); - this._setNativeNode(node._$[0]); - this._$.append(myContents); - this.triggerChangeEvent('nodeTagChange', {oldTagName: oldTagName, newTagName: this.getTagName()}); - }, + node.setData(this.getData()); + this.replaceWith(node); + return node; + }, setAttr: function(name, value, silent) { var oldVal = this.getAttr(name); @@ -135,6 +143,7 @@ var elementNodeTransformations = { var last = _.last(this.contents()); if(last && last.nodeType === Node.TEXT_NODE && node.nodeType === Node.TEXT_NODE) { last.setText(last.getText() + node.getText()); + node.detach(); return last; } else { this._$.append(node.nativeNode); @@ -146,6 +155,7 @@ var elementNodeTransformations = { var first = this.contents()[0]; if(first && first.nodeType === Node.TEXT_NODE && node.nodeType === Node.TEXT_NODE) { first.setText(node.getText() + first.getText()); + node.detach(); return first; } else { this._$.prepend(node.nativeNode); @@ -216,6 +226,7 @@ var textNodeTransformations = { before: INSERTION(function(node) { if(node.nodeType === Node.TEXT_NODE) { this.prependText(node.getText()); + node.detach(); return this; } else { return this.__super__.before(node, {silent:true}); @@ -225,12 +236,28 @@ var textNodeTransformations = { after: INSERTION(function(node) { if(node.nodeType === Node.TEXT_NODE) { this.appendText(node.getText()); + node.detach(); return this; } else { return this.__super__.after(node, {silent:true}); } }), + append: function(node) { + if(node.nodeType === Node.TEXT_NODE) { + this.appendText(node.getText()); + node.detach(); + return this; + } + }, + prepend: function(node) { + if(node.nodeType === Node.TEXT_NODE) { + this.prependText(node.getText()); + node.detach(); + return this; + } + }, + appendText: function(text) { this.nativeNode.data = this.nativeNode.data + text; this.triggerTextChangeEvent(); @@ -407,6 +434,59 @@ var documentTransformations = { this._defineDocumentProperties(insertion.ofNode._$); insertion.ofNode.triggerChangeEvent('nodeAdded'); return insertion.ofNode; + }, + deleteText: function(params) { + var ptr, next, toDetach, middle, text; + + if(params.from.node.sameNode(params.to.node)) { + ptr = params.from.node; + text = ptr.getText(); + ptr.setText(text.substr(0, params.from.offset) + text.substr(params.to.offset)); + return; + } + + // Both edge text nodes need to be edited before anything else happen in case that + // they get merged when detaching content between them. + params.from.node.setText(params.from.node.getText().substr(0, params.from.offset)); + params.to.node.setText(params.to.node.getText().substr(params.to.offset)); + + ptr = params.from.node; + next = ptr.next(); + + while(next || ptr.parent()) { + if(next) { + if(next.sameNode(params.to.node)) { + return; + } + else if(next.nodeType === Node.ELEMENT_NODE && next.containsNode(params.to.node)) { + middle = next; + break; + } else { + toDetach = next; + next = next.next(); + toDetach.detach(); + } + } else { + ptr = ptr.parent(); + next = ptr.next(); + } + } + + if(!this.containsNode(params.to.node)) { + // The end node was merged during detaching nodes above - there is nothing more left to do. + return; + } + + ptr = middle.contents()[0]; + while(ptr && !ptr.sameNode(params.to.node)) { + if(ptr.nodeType === Node.ELEMENT_NODE && ptr.containsNode(params.to.node)) { + ptr = ptr.contents()[0]; + continue; + } else { + ptr = ptr.next(); + ptr.prev().detach(); + } + } } };