04449cf3410b9d99d41f6da4e487048385841801
[fnpeditor.git] / src / smartxml / smartxml.js
1 define([
2     'libs/jquery'
3 ], function($) {
4     
5 'use strict';
6
7
8 var TEXT_NODE = Node.TEXT_NODE, ELEMENT_NODE = Node.ELEMENT_NODE;
9
10 var parseXML = function(xml) {
11     return $(xml)[0];
12 };
13
14 var Document = function(nativeNode) {
15     var $document = $(nativeNode);
16
17
18     Object.defineProperty(this, 'root', {get: function() { return new ElementNode($document[0]);}});
19 };
20
21
22 var DocumentNode = function(nativeNode) {
23     this.nativeNode = nativeNode;
24     this._$ = $(nativeNode);
25 };
26
27 $.extend(DocumentNode.prototype, {
28     detach: function() { this._$.detach(); },
29
30     sameNode: function(otherNode) {
31         return this.nativeNode === otherNode.nativeNode;
32     }
33 });
34
35 var ElementNode = function(nativeNode) {
36     DocumentNode.apply(this, arguments);
37 };
38
39 $.extend(ElementNode.prototype, DocumentNode.prototype, {
40     nodeType: Node.ELEMENT_NODE,
41
42     getTagName: function() {
43         return this.nativeNode.tagName.toLowerCase();
44     },
45
46     contents: function() {
47         var toret = [];
48         this._$.contents().each(function() {
49             if(this.nodeType === Node.ELEMENT_NODE)
50                 toret.push(new ElementNode(this));
51             else if(this.nodeType === Node.TEXT_NODE)
52                 toret.push(new TextNode(this));
53         });
54         return toret;
55     },
56
57     indexOf: function(node) {
58         return this._$.contents().index(node._$);
59     },
60
61     parent: function() {
62         return new ElementNode(this._$.parent());
63     },
64
65     getAttr: function(name) {
66         return this._$.attr(name);
67     },
68
69     setAttr: function(name, value) {
70         this._$.attr(name, value);
71     },
72
73     append: function(documentNode) {
74         this._$.append(documentNode.nativeNode);
75     },
76
77     before: function(node) {
78         this._$.before(node.nativeNode);
79     },
80
81     unwrapContent: function() {
82         var parent = this.parent();
83         if(!parent)
84             return;
85
86         var parentContents = parent.contents(),
87             myContents = this.contents(),
88             myIdx = parent.indexOf(this);
89
90         if(myContents.length === 0)
91             return this.detach();
92
93         var moveLeftRange, moveRightRange, leftMerged;
94
95         if(myIdx > 0 && (parentContents[myIdx-1].nodeType === TEXT_NODE) && (myContents[0].nodeType === TEXT_NODE)) {
96             parentContents[myIdx-1].appendText(myContents[0].getText());
97             myContents[0].detach();
98             moveLeftRange = true;
99             leftMerged = true;
100         } else {
101             leftMerged = false;
102         }
103
104         if(!(leftMerged && myContents.length === 1)) {
105             if(myIdx < parentContents.length - 1 && (parentContents[myIdx+1].nodeType === TEXT_NODE) && (myContents[myContents.length-1].nodeType === TEXT_NODE)) {
106                 parentContents[myIdx+1].prependText(myContents[myContents.length-1].getText());
107                 myContents[myContents.length-1].detach();
108                 moveRightRange = true;
109             }
110         }
111
112         var childrenLength = this.contents().length;
113         this.contents().forEach(function(child) {
114             this.before(child);
115         }.bind(this));
116
117         this.detach();
118
119         return {
120             element1: parent.contents()[myIdx + (moveLeftRange ? -1 : 0)],
121             element2: parent.contents()[myIdx + childrenLength-1 + (moveRightRange ? 1 : 0)]
122         };
123     }
124
125 });
126
127 var TextNode = function(nativeNode) {
128     DocumentNode.apply(this, arguments);
129 };
130
131 $.extend(TextNode.prototype, DocumentNode.prototype, {
132     nodeType: Node.TEXT_NODE,
133
134     getText: function() {
135         return this.nativeNode.data;
136     },
137
138     appendText: function(text) {
139         this.nativeNode.data = this.nativeNode.data + text;
140     },
141
142     prependText: function(text) {
143         this.nativeNode.data = text + this.nativeNode.data;
144     }
145 });
146
147
148 return {
149     documentFromXML: function(xml) {
150         return new Document(parseXML(xml));
151     },
152
153     elementNodeFromXML: function(xml) {
154         return new ElementNode(parseXML(xml));
155     }
156 };
157
158 });