84610b8cb33834daa32f9fe563d61e0bfa2168cd
[fnpeditor.git] / modules / documentCanvas / canvas / documentElement.js
1 define([
2 'libs/jquery-1.9.1.min',
3 'libs/underscore-min'
4 ], function($, _) {
5     
6 'use strict';
7
8
9 // DocumentElement represents a node from WLXML document rendered inside Canvas
10 var DocumentElement = function(htmlElement, canvas) {
11     if(arguments.length === 0)
12         return;
13     this.canvas = canvas;
14     this.$element = $(htmlElement);
15 }
16
17 $.extend(DocumentElement.prototype, {
18     dom: function() {
19         return this.$element;
20     },
21     parent: function() {
22         return documentElementFromHTMLElement(this.$element.parent()[0], this.canvas);
23     },
24
25     sameNode: function(other) {
26         return other && (typeof other === typeof this) && other.$element[0] === this.$element[0];
27     },
28
29     wrapWithNodeElement: function(wlxmlNode) {
30         var wrapper = DocumentNodeElement.create({tag: wlxmlNode.tag, klass: wlxmlNode.klass});
31         this.$element.replaceWith(wrapper.dom());
32         wrapper.append(this);
33         return wrapper;
34     },
35
36     detach: function() {
37         this.$element.detach();
38         this.canvas = null;
39     }
40 });
41
42
43 var DocumentNodeElement = function(htmlElement, canvas) {
44     DocumentElement.call(this, htmlElement, canvas);
45 };
46
47 var DocumentTextElement = function(htmlElement, canvas) {
48     DocumentElement.call(this, htmlElement, canvas);
49 };
50
51 DocumentNodeElement.prototype = new DocumentElement();
52 DocumentTextElement.prototype = new DocumentElement();
53
54 var manipulate = function(e, params, action) {
55     var dom;
56     if(params instanceof DocumentElement) {
57         dom = params.dom()
58     } else {
59         dom = DocumentNodeElement.createDOM(params);
60     }
61     e.$element[action](dom);
62     return documentElementFromHTMLElement(dom);
63 };
64
65 $.extend(DocumentNodeElement.prototype, {
66     append: function(params) {
67         manipulate(this, params, 'append');
68     },
69     before: function(params) {
70         manipulate(this, params, 'before');
71
72     },
73     after: function(params) {
74         manipulate(this, params, 'after');
75     },
76     children: function() {
77         var toret = [];
78         if(this instanceof DocumentTextElement)
79             return toret;
80
81
82         var elementContent = this.$element.contents();
83         var element = this;
84         elementContent.each(function(idx) {
85             var childElement = documentElementFromHTMLElement(this, element.canvas);
86             if(idx === 0 && elementContent.length > 1 && elementContent[1].nodeType === Node.ELEMENT_NODE && (childElement instanceof DocumentTextElement) && $.trim($(this).text()) === '')
87                 return true;
88             if(idx > 0 && childElement instanceof DocumentTextElement) {
89                 if(toret[toret.length-1] instanceof DocumentNodeElement && $.trim($(this).text()) === '')
90                     return true;
91             }
92             toret.push(childElement);
93         });
94         return toret;
95     },
96     childIndex: function(child) {
97         var children = this.children(),
98             toret = null;
99         children.forEach(function(c, idx) {
100             if(c.sameNode(child)) {
101                 toret = idx;
102                 return false;
103             }
104         });
105         return toret;
106     },
107     getWlxmlTag: function() {
108         return this.$element.attr('wlxml-tag');
109     },
110     setWlxmlTag: function(tag) {
111         this.$element.attr('wlxml-tag', tag);
112     },
113     getWlxmlClass: function() {
114         var klass = this.$element.attr('wlxml-class');
115         if(klass)
116             return klass.replace('-', '.');
117         return undefined;
118     },
119     setWlxmlClass: function(klass) {
120         if(klass)
121             this.$element.attr('wlxml-class', klass);
122         else
123             this.$element.removeAttr('wlxml-class');
124     },
125     is: function(what) {
126         if(what === 'list' && _.contains(['list-items', 'list-items-enum'], this.$element.attr('wlxml-class')))
127             return true;
128         return false;
129     }
130 });
131
132 DocumentNodeElement.createDOM = function(params) {
133     var dom;
134     if(params.text) {
135         dom = $(document.createTextNode(params.text));
136     } else {
137         dom = $('<div>').attr('wlxml-tag', params.tag);
138         if(params.klass)
139             dom.attr('wlxml-class', params.klass);
140     }
141     return dom;
142 };
143
144
145 DocumentNodeElement.create = function(params, canvas) {
146     return documentElementFromHTMLElement(DocumentNodeElement.createDOM(params)[0]);
147 };
148
149
150 $.extend(DocumentTextElement.prototype, {
151     setText: function(text) {
152         this.$element[0].data = text;
153     },
154     getText: function() {
155         return this.$element.text();
156     },
157     after: function(params) {
158         if(params instanceof DocumentTextElement || params.text)
159             return false;
160         var dom;
161         if(params instanceof DocumentNodeElement) {
162             dom = params.dom();
163         } else {
164             dom = DocumentNodeElement.createDOM(params);
165         }
166         this.$element.wrap('<div>');
167         this.$element.parent().after(dom[0]);
168         this.$element.unwrap();
169         return documentElementFromHTMLElement(dom[0]);
170     },
171     before: function(params) {
172         if(params instanceof DocumentTextElement || params.text)
173             return false;
174         var dom;
175         if(params instanceof DocumentNodeElement) {
176             dom = params.dom();
177         } else {
178             dom = DocumentNodeElement.createDOM(params);
179         }
180         this.$element.wrap('<div>');
181         this.$element.parent().before(dom[0]);
182         this.$element.unwrap();
183         return documentElementFromHTMLElement(dom[0]);
184     },
185     wrapWithNodeElement: function(wlxmlNode) {
186         if(typeof wlxmlNode.start === 'number' && typeof wlxmlNode.end === 'number') {
187             return this.canvas.wrapText({
188                 inside: this.parent(),
189                 textNodeIdx: this.parent().childIndex(this),
190                 offsetStart: Math.min(wlxmlNode.start, wlxmlNode.end),
191                 offsetEnd: Math.max(wlxmlNode.start, wlxmlNode.end),
192                 _with: {tag: wlxmlNode.tag, klass: wlxmlNode.klass}
193             });
194         } else {
195             return DocumentElement.prototype.wrapWithNodeElement.call(this, wlxmlNode);
196         }
197     },
198     unwrap: function() {
199         if(this.parent().children().length === 1) {
200             var parent = this.parent();
201             parent.after(this);
202             parent.detach();
203         }
204     },
205     split: function(params) {
206         var parentElement = this.parent(),
207             myIdx = parentElement.childIndex(this),
208             myCanvas = this.canvas,
209             passed = false,
210             succeedingChildren = [],
211             thisElement = this,
212             prefix = this.getText().substr(0, params.offset),
213             suffix = this.getText().substr(params.offset);
214
215         parentElement.children().forEach(function(child) {
216             if(passed)
217                 succeedingChildren.push(child);
218             if(child.sameNode(thisElement))
219                 passed = true;
220         });
221
222         if(prefix.length > 0)
223             this.setText(prefix);
224         else
225             this.detach();
226         
227         var newElement = DocumentNodeElement.create({tag: parentElement.getWlxmlTag(), klass: parentElement.getWlxmlClass()}, myCanvas);
228         parentElement.after(newElement);
229
230         if(suffix.length > 0)
231             newElement.append({text: suffix});
232         succeedingChildren.forEach(function(child) {
233             newElement.append(child);
234         });
235     }
236 });
237
238 var documentElementFromHTMLElement = function(htmlElement, canvas) {
239     // if(!canvas)
240     //    throw 'no canvas specified';
241     if(htmlElement.nodeType === Node.ELEMENT_NODE)
242         return new DocumentNodeElement(htmlElement, canvas);
243     if(htmlElement.nodeType === Node.TEXT_NODE)
244         return new DocumentTextElement(htmlElement, canvas);
245 };
246
247 return {
248     wrap: function(htmlElement, canvas) {
249         return documentElementFromHTMLElement(htmlElement, canvas);
250     },
251     DocumentElement: DocumentElement,
252     DocumentNodeElement: DocumentNodeElement,
253     DocumentTextElement: DocumentTextElement
254 };
255
256 });