6 ], function($, _, Backbone, events) {
11 var TEXT_NODE = Node.TEXT_NODE;
14 var DocumentNode = function(nativeNode, document) {
16 throw new Error('undefined document for a node');
18 this.document = document;
19 this.nativeNode = nativeNode;
20 this._$ = $(nativeNode);
23 $.extend(DocumentNode.prototype, {
24 detach: function() { this._$.detach(); },
26 sameNode: function(otherNode) {
27 return this.nativeNode === otherNode.nativeNode;
31 return this.nativeNode.parentNode ? this.document.createElementNode(this.nativeNode.parentNode) : null;
34 before: function(node) {
35 this._$.before(node.nativeNode);
38 wrapWith: function(node) {
45 triggerChangeEvent: function(type, metaData) {
46 var event = new events.ChangeEvent(type, $.extend({node: this}, metaData || {}));
47 this.document.trigger('change', event);
51 var ElementNode = function(nativeNode, document) {
52 DocumentNode.call(this, nativeNode, document);
55 $.extend(ElementNode.prototype, DocumentNode.prototype, {
56 nodeType: Node.ELEMENT_NODE,
58 setData: function(key, value) {
59 if(value !== undefined) {
60 this._$.data(key, value);
62 this._$.removeData(_.keys(this._$.data()));
67 getData: function(key) {
69 return this._$.data(key);
71 return this._$.data();
74 getTagName: function() {
75 return this.nativeNode.tagName.toLowerCase();
78 contents: function() {
80 document = this.document;
81 this._$.contents().each(function() {
82 if(this.nodeType === Node.ELEMENT_NODE) {
83 toret.push(document.createElementNode(this));
85 else if(this.nodeType === Node.TEXT_NODE) {
86 toret.push(document.createTextNode(this));
92 indexOf: function(node) {
93 return this._$.contents().index(node._$);
96 getAttr: function(name) {
97 return this._$.attr(name);
100 setAttr: function(name, value) {
101 var oldVal = this.getAttr(name);
102 this._$.attr(name, value);
103 this.triggerChangeEvent('nodeAttrChange', {attr: name, oldVal: oldVal, newVal: value});
106 getAttrs: function() {
108 for(var i = 0; i < this.nativeNode.attributes.length; i++) {
109 toret.push(this.nativeNode.attributes[i]);
114 append: function(documentNode) {
115 this._$.append(documentNode.nativeNode);
118 unwrapContent: function() {
119 var parent = this.parent();
124 var parentContents = parent.contents(),
125 myContents = this.contents(),
126 myIdx = parent.indexOf(this);
128 if(myContents.length === 0) {
129 return this.detach();
132 var moveLeftRange, moveRightRange, leftMerged;
134 if(myIdx > 0 && (parentContents[myIdx-1].nodeType === TEXT_NODE) && (myContents[0].nodeType === TEXT_NODE)) {
135 parentContents[myIdx-1].appendText(myContents[0].getText());
136 myContents[0].detach();
137 moveLeftRange = true;
143 if(!(leftMerged && myContents.length === 1)) {
144 if(myIdx < parentContents.length - 1 && (parentContents[myIdx+1].nodeType === TEXT_NODE) && (myContents[myContents.length-1].nodeType === TEXT_NODE)) {
145 parentContents[myIdx+1].prependText(myContents[myContents.length-1].getText());
146 myContents[myContents.length-1].detach();
147 moveRightRange = true;
151 var childrenLength = this.contents().length;
152 this.contents().forEach(function(child) {
159 element1: parent.contents()[myIdx + (moveLeftRange ? -1 : 0)],
160 element2: parent.contents()[myIdx + childrenLength-1 + (moveRightRange ? 1 : 0)]
165 var wrapper = $('<div>');
166 wrapper.append(this._$);
167 return wrapper.html();
171 var TextNode = function(nativeNode, document) {
172 DocumentNode.call(this, nativeNode, document);
175 $.extend(TextNode.prototype, DocumentNode.prototype, {
176 nodeType: Node.TEXT_NODE,
178 getText: function() {
179 return this.nativeNode.data;
182 appendText: function(text) {
183 this.nativeNode.data = this.nativeNode.data + text;
186 prependText: function(text) {
187 this.nativeNode.data = text + this.nativeNode.data;
192 var parseXML = function(xml) {
196 var Document = function(xml) {
197 var $document = $(parseXML(xml));
200 Object.defineProperty(this, 'root', {get: function() {
201 return doc.createElementNode($document[0]);
203 Object.defineProperty(this, 'dom', {get: function() {
207 $.extend(Document.prototype, Backbone.Events, {
208 ElementNodeFactory: ElementNode,
209 TextNodeFactory: TextNode,
211 createElementNode: function(nativeNode) {
212 return new this.ElementNodeFactory(nativeNode, this);
215 createTextNode: function(nativeNode) {
216 return new this.TextNodeFactory(nativeNode, this);
220 return this.root.toXML();
226 documentFromXML: function(xml) {
227 return new Document(parseXML(xml));
230 elementNodeFromXML: function(xml) {
231 return this.documentFromXML(xml).root;
235 DocumentNode: DocumentNode,
236 ElementNode: ElementNode