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