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 setData: function(key, value) {
58 this._$.data(key, value);
60 getData: function(key) {
61 return this._$.data(key);
64 getTagName: function() {
65 return this.nativeNode.tagName.toLowerCase();
68 contents: function() {
70 document = this.document;
71 this._$.contents().each(function() {
72 if(this.nodeType === Node.ELEMENT_NODE) {
73 toret.push(document.createElementNode(this));
75 else if(this.nodeType === Node.TEXT_NODE) {
76 toret.push(document.createTextNode(this));
82 indexOf: function(node) {
83 return this._$.contents().index(node._$);
86 getAttr: function(name) {
87 return this._$.attr(name);
90 setAttr: function(name, value) {
91 var oldVal = this.getAttr(name);
92 this._$.attr(name, value);
93 this.triggerChangeEvent('nodeAttrChange', {attr: name, oldVal: oldVal, newVal: value});
96 getAttrs: function() {
98 for(var i = 0; i < this.nativeNode.attributes.length; i++) {
99 toret.push(this.nativeNode.attributes[i]);
104 append: function(documentNode) {
105 this._$.append(documentNode.nativeNode);
108 unwrapContent: function() {
109 var parent = this.parent();
114 var parentContents = parent.contents(),
115 myContents = this.contents(),
116 myIdx = parent.indexOf(this);
118 if(myContents.length === 0) {
119 return this.detach();
122 var moveLeftRange, moveRightRange, leftMerged;
124 if(myIdx > 0 && (parentContents[myIdx-1].nodeType === TEXT_NODE) && (myContents[0].nodeType === TEXT_NODE)) {
125 parentContents[myIdx-1].appendText(myContents[0].getText());
126 myContents[0].detach();
127 moveLeftRange = true;
133 if(!(leftMerged && myContents.length === 1)) {
134 if(myIdx < parentContents.length - 1 && (parentContents[myIdx+1].nodeType === TEXT_NODE) && (myContents[myContents.length-1].nodeType === TEXT_NODE)) {
135 parentContents[myIdx+1].prependText(myContents[myContents.length-1].getText());
136 myContents[myContents.length-1].detach();
137 moveRightRange = true;
141 var childrenLength = this.contents().length;
142 this.contents().forEach(function(child) {
149 element1: parent.contents()[myIdx + (moveLeftRange ? -1 : 0)],
150 element2: parent.contents()[myIdx + childrenLength-1 + (moveRightRange ? 1 : 0)]
155 var wrapper = $('<div>');
156 wrapper.append(this._$);
157 return wrapper.html();
161 var TextNode = function(nativeNode, document) {
162 DocumentNode.call(this, nativeNode, document);
165 $.extend(TextNode.prototype, DocumentNode.prototype, {
166 nodeType: Node.TEXT_NODE,
168 getText: function() {
169 return this.nativeNode.data;
172 appendText: function(text) {
173 this.nativeNode.data = this.nativeNode.data + text;
176 prependText: function(text) {
177 this.nativeNode.data = text + this.nativeNode.data;
182 var parseXML = function(xml) {
186 var Document = function(xml) {
187 var $document = $(parseXML(xml));
190 Object.defineProperty(this, 'root', {get: function() {
191 return doc.createElementNode($document[0]);
193 Object.defineProperty(this, 'dom', {get: function() {
197 $.extend(Document.prototype, Backbone.Events, {
198 ElementNodeFactory: ElementNode,
199 TextNodeFactory: TextNode,
201 createElementNode: function(nativeNode) {
202 return new this.ElementNodeFactory(nativeNode, this);
205 createTextNode: function(nativeNode) {
206 return new this.TextNodeFactory(nativeNode, this);
210 return this.root.toXML();
216 documentFromXML: function(xml) {
217 return new Document(parseXML(xml));
220 elementNodeFromXML: function(xml) {
221 return this.documentFromXML(xml).root;
225 DocumentNode: DocumentNode,
226 ElementNode: ElementNode