b7fc3f1eae58835d01849857ae25e79e9c71d23e
[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     append: function(documentNode) {
66         this._$.append(documentNode.nativeNode);
67     },
68
69     before: function(node) {
70         this._$.before(node.nativeNode);
71     },
72
73     unwrapContent: function() {
74         var parent = this.parent();
75         if(!parent)
76             return;
77
78         var parentContents = parent.contents(),
79             myContents = this.contents(),
80             myIdx = parent.indexOf(this);
81
82         if(myContents.length === 0)
83             return this.detach();
84
85         var moveLeftRange, moveRightRange, leftMerged;
86
87         if(myIdx > 0 && (parentContents[myIdx-1].nodeType === TEXT_NODE) && (myContents[0].nodeType === TEXT_NODE)) {
88             parentContents[myIdx-1].appendText(myContents[0].getText());
89             myContents[0].detach();
90             moveLeftRange = true;
91             leftMerged = true;
92         } else {
93             leftMerged = false;
94         }
95
96         if(!(leftMerged && myContents.length === 1)) {
97             if(myIdx < parentContents.length - 1 && (parentContents[myIdx+1].nodeType === TEXT_NODE) && (myContents[myContents.length-1].nodeType === TEXT_NODE)) {
98                 parentContents[myIdx+1].prependText(myContents[myContents.length-1].getText());
99                 myContents[myContents.length-1].detach();
100                 moveRightRange = true;
101             }
102         }
103
104         var childrenLength = this.contents().length;
105         this.contents().forEach(function(child) {
106             this.before(child);
107         }.bind(this));
108
109         this.detach();
110
111         return {
112             element1: parent.contents()[myIdx + (moveLeftRange ? -1 : 0)],
113             element2: parent.contents()[myIdx + childrenLength-1 + (moveRightRange ? 1 : 0)]
114         };
115     }
116
117 });
118
119 var TextNode = function(nativeNode) {
120     DocumentNode.apply(this, arguments);
121 };
122
123 $.extend(TextNode.prototype, DocumentNode.prototype, {
124     nodeType: Node.TEXT_NODE,
125
126     getText: function() {
127         return this.nativeNode.data;
128     },
129
130     appendText: function(text) {
131         this.nativeNode.data = this.nativeNode.data + text;
132     },
133
134     prependText: function(text) {
135         this.nativeNode.data = text + this.nativeNode.data;
136     }
137 });
138
139
140 return {
141     documentFromXML: function(xml) {
142         return new Document(parseXML(xml));
143     },
144
145     elementNodeFromXML: function(xml) {
146         return new ElementNode(parseXML(xml));
147     }
148 };
149
150 });