Wrapping text spanning multiple sibling DocumentTextNodes
[fnpeditor.git] / modules / documentCanvas / canvas / documentElement.js
1 define([
2 'libs/jquery-1.9.1.min'
3 ], function($) {
4     
5 'use strict';
6
7
8 // DocumentElement represents a node from WLXML document rendered inside Canvas
9 var DocumentElement = function(htmlElement, canvas) {
10     if(arguments.length === 0)
11         return;
12     this.canvas = canvas;
13     this.$element = $(htmlElement);
14     this.wlxmlTag = this.$element.prop('tagName');
15 };
16
17 $.extend(DocumentElement.prototype, {
18     dom: function() {
19         return this.$element;
20     },
21     children: function() {
22         var toret = [];
23         if(this instanceof DocumentTextElement)
24             return toret;
25
26
27         var elementContent = this.$element.contents();
28         var element = this;
29         elementContent.each(function(idx) {
30             var childElement = documentElementFromHTMLElement(this, element.canvas);
31             if(idx === 0 && elementContent.length > 1 && elementContent[1].nodeType === Node.ELEMENT_NODE && (childElement instanceof DocumentTextElement) && $.trim($(this).text()) === '')
32                 return true;
33             if(idx > 0 && childElement instanceof DocumentTextElement) {
34                 if(toret[toret.length-1] instanceof DocumentNodeElement && $.trim($(this).text()) === '')
35                     return true;
36             }
37             toret.push(childElement);
38         });
39         return toret;
40     },
41     parent: function() {
42         return documentElementFromHTMLElement(this.$element.parent()[0], this.canvas);
43     },
44
45     sameNode: function(other) {
46         return other && (typeof other === typeof this) && other.$element[0] === this.$element[0];
47     },
48
49     wrapWithNodeElement: function(wlxmlNode) {
50         this.$element.wrap($('<' + wlxmlNode.tag + ' class="' + wlxmlNode.klass + '"">')[0]);
51         return documentElementFromHTMLElement(this.$element.parent().get(0), this.canvas);
52     },
53
54     childIndex: function(child) {
55         var children = this.children(),
56             toret = null;
57         children.forEach(function(c, idx) {
58             if(c.sameNode(child)) {
59                 toret = idx;
60                 return false;
61             }
62         });
63         return toret;
64     },
65
66     detach: function() {
67         this.$element.detach();
68         this.canvas = null;
69     }
70 });
71
72
73 var DocumentNodeElement = function(htmlElement, canvas) {
74     DocumentElement.call(this, htmlElement, canvas);
75 };
76
77 var DocumentTextElement = function(htmlElement, canvas) {
78     DocumentElement.call(this, htmlElement, canvas);
79 };
80
81 DocumentNodeElement.prototype = new DocumentElement();
82 DocumentTextElement.prototype = new DocumentElement();
83
84 var manipulate = function(e, params, action) {
85     var dom;
86     if(params instanceof DocumentElement) {
87         dom = params.dom()
88     } else {
89         dom = DocumentNodeElement.createDOM(params);
90     }
91     e.$element[action](dom);
92     return documentElementFromHTMLElement(dom);
93 };
94
95 $.extend(DocumentNodeElement.prototype, {
96     append: function(params) {
97         manipulate(this, params, 'append');
98     },
99     before: function(params) {
100         manipulate(this, params, 'before');
101
102     },
103     after: function(params) {
104         manipulate(this, params, 'after');
105     }
106 });
107
108 DocumentNodeElement.createDOM = function(params) {
109     var dom;
110     if(params.text) {
111         dom = $(document.createTextNode(params.text));
112     } else {
113         dom = $('<' + params.tag + '>');
114         if(params.klass)
115             dom.attr('class', params.klass);
116     }
117     return dom;
118 };
119
120
121 DocumentNodeElement.create = function(params) {
122     return documentElementFromHTMLElement(DocumentNodeElement.createDOM(params)[0]);
123 };
124
125
126 $.extend(DocumentTextElement.prototype, {
127     setText: function(text) {
128         this.$element[0].data = text;
129     },
130     getText: function() {
131         return this.$element.text();
132     },
133     after: function(params) {
134         if(params instanceof DocumentTextElement || params.text)
135             return false;
136         var dom;
137         if(params instanceof DocumentNodeElement) {
138             dom = params.dom();
139         } else {
140             dom = DocumentNodeElement.createDOM(params);
141         }
142         this.$element.wrap('<div>');
143         this.$element.parent().after(dom[0]);
144         this.$element.unwrap();
145         return documentElementFromHTMLElement(dom[0]);
146     },
147     wrapWithNodeElement: function(wlxmlNode) {
148         if(wlxmlNode.start && wlxmlNode.end) {
149             return this.canvas.wrapText({
150                 inside: this.parent(),
151                 textNodeIdx: this.parent().childIndex(this),
152                 offsetStart: wlxmlNode.start,
153                 offsetEnd: wlxmlNode.end,
154                 _with: {tag: wlxmlNode.tag, klass: wlxmlNode.klass}
155             });
156         } else {
157             return DocumentElement.prototype.wrapWithNodeElement.call(this, wlxmlNode);
158         }
159     }
160 });
161
162 var documentElementFromHTMLElement = function(htmlElement, canvas) {
163     // if(!canvas)
164     //    throw 'no canvas specified';
165     if(htmlElement.nodeType === Node.ELEMENT_NODE)
166         return new DocumentNodeElement(htmlElement, canvas);
167     if(htmlElement.nodeType === Node.TEXT_NODE)
168         return new DocumentTextElement(htmlElement, canvas);
169 };
170
171 return {
172     wrap: function(htmlElement, canvas) {
173         return documentElementFromHTMLElement(htmlElement, canvas);
174     },
175     DocumentElement: DocumentElement,
176     DocumentNodeElement: DocumentNodeElement,
177     DocumentTextElement: DocumentTextElement
178 };
179
180 });