5 ], function($, Backbone, events) {
10 var TEXT_NODE = Node.TEXT_NODE;
13 var DocumentNode = function(nativeNode, document) {
15 throw new Error('undefined document for a node');
17 this.document = document;
18 this.nativeNode = nativeNode;
19 this._$ = $(nativeNode);
22 $.extend(DocumentNode.prototype, {
23 detach: function() { this._$.detach(); },
25 sameNode: function(otherNode) {
26 return this.nativeNode === otherNode.nativeNode;
30 return this.nativeNode.parentNode ? this.document.createElementNode(this.nativeNode.parentNode) : null;
33 before: function(node) {
34 this._$.before(node.nativeNode);
37 wrapWith: function(node) {
44 triggerChangeEvent: function(type, metaData) {
45 var event = new events.ChangeEvent(type, $.extend({node: this}, metaData || {}));
46 this.document.trigger('change', event);
50 var ElementNode = function(nativeNode, document) {
51 DocumentNode.call(this, nativeNode, document);
54 $.extend(ElementNode.prototype, DocumentNode.prototype, {
55 nodeType: Node.ELEMENT_NODE,
57 getTagName: function() {
58 return this.nativeNode.tagName.toLowerCase();
61 contents: function() {
63 document = this.document;
64 this._$.contents().each(function() {
65 if(this.nodeType === Node.ELEMENT_NODE) {
66 toret.push(document.createElementNode(this));
68 else if(this.nodeType === Node.TEXT_NODE) {
69 toret.push(document.createTextNode(this));
75 indexOf: function(node) {
76 return this._$.contents().index(node._$);
79 getAttr: function(name) {
80 return this._$.attr(name);
83 setAttr: function(name, value) {
84 var oldVal = this.getAttr(name);
85 this._$.attr(name, value);
86 this.triggerChangeEvent('nodeAttrChange', {attr: name, oldVal: oldVal, newVal: value});
89 getAttrs: function() {
91 for(var i = 0; i < this.nativeNode.attributes.length; i++) {
92 toret.push(this.nativeNode.attributes[i]);
97 append: function(documentNode) {
98 this._$.append(documentNode.nativeNode);
101 unwrapContent: function() {
102 var parent = this.parent();
107 var parentContents = parent.contents(),
108 myContents = this.contents(),
109 myIdx = parent.indexOf(this);
111 if(myContents.length === 0) {
112 return this.detach();
115 var moveLeftRange, moveRightRange, leftMerged;
117 if(myIdx > 0 && (parentContents[myIdx-1].nodeType === TEXT_NODE) && (myContents[0].nodeType === TEXT_NODE)) {
118 parentContents[myIdx-1].appendText(myContents[0].getText());
119 myContents[0].detach();
120 moveLeftRange = true;
126 if(!(leftMerged && myContents.length === 1)) {
127 if(myIdx < parentContents.length - 1 && (parentContents[myIdx+1].nodeType === TEXT_NODE) && (myContents[myContents.length-1].nodeType === TEXT_NODE)) {
128 parentContents[myIdx+1].prependText(myContents[myContents.length-1].getText());
129 myContents[myContents.length-1].detach();
130 moveRightRange = true;
134 var childrenLength = this.contents().length;
135 this.contents().forEach(function(child) {
142 element1: parent.contents()[myIdx + (moveLeftRange ? -1 : 0)],
143 element2: parent.contents()[myIdx + childrenLength-1 + (moveRightRange ? 1 : 0)]
148 var wrapper = $('<div>');
149 wrapper.append(this._$);
150 return wrapper.html();
154 var TextNode = function(nativeNode, document) {
155 DocumentNode.call(this, nativeNode, document);
158 $.extend(TextNode.prototype, DocumentNode.prototype, {
159 nodeType: Node.TEXT_NODE,
161 getText: function() {
162 return this.nativeNode.data;
165 appendText: function(text) {
166 this.nativeNode.data = this.nativeNode.data + text;
169 prependText: function(text) {
170 this.nativeNode.data = text + this.nativeNode.data;
175 var parseXML = function(xml) {
179 var Document = function(xml) {
180 var $document = $(parseXML(xml));
183 Object.defineProperty(this, 'root', {get: function() {
184 return doc.createElementNode($document[0]);
186 Object.defineProperty(this, 'dom', {get: function() {
190 $.extend(Document.prototype, Backbone.Events, {
191 ElementNodeFactory: ElementNode,
192 TextNodeFactory: TextNode,
194 createElementNode: function(nativeNode) {
195 return new this.ElementNodeFactory(nativeNode, this);
198 createTextNode: function(nativeNode) {
199 return new this.TextNodeFactory(nativeNode, this);
203 return this.root.toXML();
209 documentFromXML: function(xml) {
210 return new Document(parseXML(xml));
213 elementNodeFromXML: function(xml) {
214 return this.documentFromXML(xml).root;
218 DocumentNode: DocumentNode,
219 ElementNode: ElementNode