b2e25ed9f0e94fe3ced97f1ab9f89fc1bd0c8128
[fnpeditor.git] / src / editor / modules / documentCanvas / canvas / documentElement.js
1 define([
2 'libs/jquery',
3 'libs/underscore',
4 'modules/documentCanvas/canvas/utils'
5 ], function($, _, utils) {
6     
7 'use strict';
8 /* global Node:false */
9
10 // DocumentElement represents a text or an element node from WLXML document rendered inside Canvas
11 var DocumentElement = function(wlxmlNode, canvas) {
12     this.wlxmlNode = wlxmlNode;
13     this.canvas = canvas;
14
15     this.$element = this.createDOM();
16     this.$element.data('canvas-element', this);
17 };
18
19 $.extend(DocumentElement.prototype, {
20     refreshPath: function() {
21         this.parents().forEach(function(parent) {
22             parent.refresh();
23         });
24         this.refresh();
25     },
26     refresh: function() {
27         // noop
28     },
29     dom: function() {
30         return this.$element;
31     },
32     parent: function() {
33         var parents = this.$element.parents('[document-node-element]');
34         if(parents.length) {
35             return this.canvas.getDocumentElement(parents[0]);
36         }
37         return null;
38     },
39
40     parents: function() {
41         var parents = [],
42             parent = this.parent();
43         while(parent) {
44             parents.push(parent);
45             parent = parent.parent();
46         }
47         return parents;
48     },
49
50     sameNode: function(other) {
51         return other && (typeof other === typeof this) && other.dom()[0] === this.dom()[0];
52     },
53
54     trigger: function() {
55         this.canvas.eventBus.trigger.apply(this.canvas.eventBus, Array.prototype.slice.call(arguments, 0));
56     }
57
58
59 });
60
61
62 // DocumentNodeElement represents an element node from WLXML document rendered inside Canvas
63 var DocumentNodeElement = function(wlxmlNode, canvas) {
64     DocumentElement.call(this, wlxmlNode, canvas);
65     wlxmlNode.setData('canvasElement', this);
66     this.init(this.$element);
67 };
68
69
70 var manipulate = function(e, params, action) {
71     var element;
72     if(params instanceof DocumentElement) {
73         element = params;
74     } else {
75         element = e.canvas.createElement(params);
76     }
77     e.dom()[action](element.dom());
78     e.refreshPath();
79     return element;
80 };
81
82 DocumentNodeElement.prototype = Object.create(DocumentElement.prototype);
83
84
85 $.extend(DocumentNodeElement.prototype, {
86     defaultDisplayStyle: 'block',
87     addWidget: function(widget) {
88         this.$element.children('.canvas-widgets').append(widget.DOM ? widget.DOM : widget);
89     },
90     clearWidgets: function() {
91         this.$element.children('.canvas-widgets').empty();
92     },
93     handle: function(event) {
94         var method = 'on' + event.type[0].toUpperCase() + event.type.substr(1);
95         if(this[method]) {
96             this[method](event);
97         }
98     },
99     createDOM: function() {
100         var wrapper = $('<div>').attr('document-node-element', ''),
101             widgetsContainer = $('<div>')
102                 .addClass('canvas-widgets')
103                 .attr('contenteditable', false),
104             contentContainer = $('<div>')
105                 .attr('document-element-content', '');
106         
107         wrapper.append(widgetsContainer, contentContainer);
108         widgetsContainer.find('*').add(widgetsContainer).attr('tabindex', -1);
109         return wrapper;
110     },
111     _container: function() {
112         return this.dom().children('[document-element-content]');
113     },
114     detach: function() {
115         var parents = this.parents();
116         this.dom().detach();
117         this.canvas = null;
118         if(parents[0]) {
119             parents[0].refreshPath();
120         }
121          return this;
122     },
123     before: function(params) {
124         return manipulate(this, params, 'before');
125
126     },
127     after: function(params) {
128         return manipulate(this, params, 'after');
129     },
130
131     toggleLabel: function(toggle) {
132         var displayCss = toggle ? 'inline-block' : 'none';
133         var label = this.dom().children('.canvas-widgets').find('.canvas-widget-label');
134         label.css('display', displayCss);
135         this.toggleHighlight(toggle);
136     },
137
138     toggleHighlight: function(toggle) {
139         this._container().toggleClass('highlighted-element', toggle);
140     },
141
142     isBlock: function() {
143         return this.dom().css('display') === 'block';
144     },
145
146     displayAsBlock: function() {
147         this.dom().css('display', 'block');
148         this._container().css('display', 'block');
149     },
150     displayInline: function() {
151         this.dom().css('display', 'inline');
152         this._container().css('display', 'inline');
153     },
154     displayAs: function(what) {
155         // [this.dom(), this._container()].forEach(e) {
156         //     var isBlock = window.getComputedStyle(e).display === 'block';
157         //     if(!isBlock && what === 'block') {
158         //         e.css('display', what);
159         //     } else if(isBlock && what === 'inline') {
160         //         e.css('display')
161         //     }
162         // })
163         this.dom().css('display', what);
164         this._container().css('display', what);
165     }
166 });
167
168
169 // DocumentNodeElement represents a text node from WLXML document rendered inside Canvas
170 var DocumentTextElement = function(wlxmlTextNode, canvas) {
171     DocumentElement.call(this, wlxmlTextNode, canvas);
172 };
173
174 $.extend(DocumentTextElement, {
175     isContentContainer: function(htmlElement) {
176         return htmlElement.nodeType === Node.TEXT_NODE && $(htmlElement).parent().is('[document-text-element]');
177     }
178 });
179
180 DocumentTextElement.prototype = Object.create(DocumentElement.prototype);
181
182 $.extend(DocumentTextElement.prototype, {
183     createDOM: function() {
184         var dom = $('<div>')
185             .attr('document-text-element', '')
186             .text(this.wlxmlNode.getText() || utils.unicode.ZWS);
187         return dom;
188     },
189     detach: function() {
190         this.dom().detach();
191         this.canvas = null;
192         return this;
193     },
194     setText: function(text) {
195         this.dom().contents()[0].data = text;
196     },
197     getText: function(options) {
198         options = _.extend({raw: false}, options || {});
199         var toret = this.dom().text();
200         if(!options.raw) {
201             toret = toret.replace(utils.unicode.ZWS, '');
202         }
203         return toret;
204     },
205     isEmpty: function() {
206         // Having at least Zero Width Space is guaranteed be Content Observer
207         return this.dom().contents()[0].data === utils.unicode.ZWS;
208     },
209     after: function(params) {
210         if(params instanceof DocumentTextElement || params.text) {
211             return false;
212         }
213         var element;
214         if(params instanceof DocumentNodeElement) {
215             element = params;
216         } else {
217             element = this.canvas.createElement(params);
218         }
219         this.dom().wrap('<div>');
220         this.dom().parent().after(element.dom());
221         this.dom().unwrap();
222         this.refreshPath();
223         return element;
224     },
225     before: function(params) {
226         if(params instanceof DocumentTextElement || params.text) {
227             return false;
228         }
229         var element;
230         if(params instanceof DocumentNodeElement) {
231             element = params;
232         } else {
233             element = this.canvas.createElement(params);
234         }
235         this.dom().wrap('<div>');
236         this.dom().parent().before(element.dom());
237         this.dom().unwrap();
238         this.refreshPath();
239         return element;
240     },
241
242     toggleHighlight: function() {
243         // do nothing for now
244     },
245     children: function() {
246         return [];
247     }
248
249 });
250
251
252 return {
253     DocumentElement: DocumentElement,
254     DocumentNodeElement: DocumentNodeElement,
255     DocumentTextElement: DocumentTextElement
256 };
257
258 });