8eeca1a4aed57873f60c4c3a1ed1e81a29ffad5a
[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 ElementNode = function(nativeNode) {
23     this.nativeNode = nativeNode;
24     this._$ = $(nativeNode);
25 };
26
27 $.extend(ElementNode.prototype, {
28     nodeType: Node.ELEMENT_NODE,
29
30     getTagName: function() {
31         return this.nativeNode.tagName.toLowerCase();
32     },
33
34     append: function(documentNode) {
35         this._$.append(documentNode.nativeNode);
36     },
37
38     before: function(node) {
39         this._$.before(node.nativeNode);
40     },
41
42     contents: function() {
43         var toret = [];
44         this._$.contents().each(function() {
45             if(this.nodeType === Node.ELEMENT_NODE)
46                 toret.push(new ElementNode(this));
47             else if(this.nodeType === Node.TEXT_NODE)
48                 toret.push(new TextNode(this));
49         });
50         return toret;
51     },
52
53
54     sameNode: function(otherNode) {
55         return this.nativeNode === otherNode.nativeNode;
56     },
57
58     indexOf: function(node) {
59         return this._$.contents().index(node._$);
60     },
61
62     detach: function() {
63         this._$.detach();
64     },
65
66     parent: function() {
67         return new ElementNode(this._$.parent());
68     },
69
70     unwrapContent: function() {
71         var parent = this.parent();
72         if(!parent)
73             return;
74
75         var parentContents = parent.contents(),
76             myContents = this.contents(),
77             myIdx = parent.indexOf(this);
78
79         if(myContents.length === 0)
80             return this.detach();
81
82         var moveLeftRange, moveRightRange, leftMerged;
83
84         if(myIdx > 0 && (parentContents[myIdx-1].nodeType === TEXT_NODE) && (myContents[0].nodeType === TEXT_NODE)) {
85             parentContents[myIdx-1].appendText(myContents[0].getText());
86             myContents[0].detach();
87             moveLeftRange = true;
88             leftMerged = true;
89         } else {
90             leftMerged = false;
91         }
92
93         if(!(leftMerged && myContents.length === 1)) {
94             if(myIdx < parentContents.length - 1 && (parentContents[myIdx+1].nodeType === TEXT_NODE) && (myContents[myContents.length-1].nodeType === TEXT_NODE)) {
95                 parentContents[myIdx+1].prependText(myContents[myContents.length-1].getText());
96                 myContents[myContents.length-1].detach();
97                 moveRightRange = true;
98             }
99         }
100
101         var childrenLength = this.contents().length;
102         this.contents().forEach(function(child) {
103             this.before(child);
104         }.bind(this));
105
106         this.detach();
107
108         return {
109             element1: parent.contents()[myIdx + (moveLeftRange ? -1 : 0)],
110             element2: parent.contents()[myIdx + childrenLength-1 + (moveRightRange ? 1 : 0)]
111         };
112     }
113
114 });
115
116 var TextNode = function(nativeNode) {
117     this.nativeNode = nativeNode;
118     this._$ = $(nativeNode);
119 }
120
121 $.extend(TextNode.prototype, {
122     nodeType: Node.TEXT_NODE,
123
124     detach: function() {
125         this._$.detach();
126     },
127
128     getText: function() {
129         return this.nativeNode.data;
130     },
131
132     appendText: function(text) {
133         this.nativeNode.data = this.nativeNode.data + text;
134     },
135
136     prependText: function(text) {
137         this.nativeNode.data = text + this.nativeNode.data;
138     }
139 })
140
141
142 return {
143     documentFromXML: function(xml) {
144         return new Document(parseXML(xml));
145     },
146
147     elementNodeFromXML: function(xml) {
148         return new ElementNode(parseXML(xml));
149     }
150 };
151
152 });