bbb4b0793c1687a4c54a5b7f0d3004ab12e22a5c
[fnpeditor.git] / src / editor / modules / documentCanvas / canvas / genericElement.js
1 define(function(require) {
2     
3 'use strict';
4
5 var $ = require('libs/jquery'),
6     _ = require('libs/underscore'),
7     documentElement = require('./documentElement'),
8     utils = require('./utils'),
9     wlxmlUtils = require('utils/wlxml');
10
11 var labelWidget = function(tag, klass) {
12     return $('<span>')
13         .addClass('canvas-widget canvas-widget-label')
14         .text(wlxmlUtils.getTagLabel(tag) + (klass ? ' / ' + wlxmlUtils.getClassLabel(klass) : ''));
15 };
16 void(labelWidget); // for linters; labelWidget is unused on purpose for now
17
18 var DocumentNodeElement = documentElement.DocumentNodeElement;
19
20 var generic = Object.create(DocumentNodeElement.prototype);
21
22 $.extend(generic, {
23     init: function() {
24         DocumentNodeElement.prototype.init.call(this);
25         this._container()
26             .attr('wlxml-tag', this.wlxmlNode.getTagName());
27         this.setWlxmlClass(this.wlxmlNode.getClass());
28         this.wlxmlNode.contents().forEach(function(node) {
29             this._container().append(this.canvas.createElement(node).dom);
30         }.bind(this));
31         this.refresh();
32     },
33     
34     refresh: function() {
35         if(this.wlxmlNode.getTagName() === 'span') {
36             if(this.containsBlock()) {
37                 this.displayAsBlock();
38             } else {
39                 this.displayInline();
40             }
41         } else {
42             this.displayAsBlock();
43         }
44     },
45
46     getFirst: function(e1, e2) {
47         var idx1 = this.childIndex(e1),
48             idx2 = this.childIndex(e2);
49         if(e1 === null || e2 === null) {
50             return undefined;
51         }
52         return idx1 <= idx2 ? e1: e2;
53     },
54
55     children: function() {
56         var element = this,
57             toret = [];
58         this._container().contents().each(function() {
59             var childElement = element.canvas.getDocumentElement(this);
60             if(childElement === undefined) {
61                 return true;
62             }
63
64             toret.push(childElement);
65         });
66         return toret;
67     },
68
69     getVerticallyFirstTextElement: function() {
70         var toret;
71         this.children().some(function(child) {
72             if(child instanceof documentElement.DocumentTextElement) {
73                 toret = child;
74                 return true; // break
75             } else {
76                 toret = child.getVerticallyFirstTextElement();
77                 if(toret) {
78                     return true; // break
79                 }
80             }
81         });
82         return toret;
83     },
84
85     onNodeAttrChange: function(event) {
86         if(event.meta.attr === 'class') {
87             this.setWlxmlClass(event.meta.newVal); //
88         }
89     },
90     onNodeAdded: function(event) {
91         if(event.meta.node.isRoot()) {
92             this.canvas.reloadRoot();
93             return;
94         }
95
96         var nodeIndex = event.meta.node.getIndex(),
97             referenceElement, referenceAction, actionArg;
98
99         if(nodeIndex === 0) {
100             referenceElement = this;
101             referenceAction = 'prepend';
102         } else {
103             referenceElement = this.children()[nodeIndex-1];
104             referenceAction = 'after';
105         }
106       
107         if(event.meta.move) {
108             /* Let's check if this node had its own canvas element and it's accessible. */
109             actionArg = utils.getElementForElementRootNode(event.meta.node);
110             if(actionArg && actionArg.sameNode(referenceElement)) {
111                 referenceElement = this.children()[nodeIndex];
112             }
113         }
114         if(!actionArg) {
115             actionArg = event.meta.node;
116         }
117
118         referenceElement[referenceAction](actionArg);
119     },
120     onNodeDetached: function(event) {
121         if(event.meta.node.sameNode(this)) {
122             this.detach();
123         } else {
124             this.children().some(function(child) {
125                 if(child.wlxmlNode.sameNode(event.meta.node)) {
126                     child.detach();
127                     return true;
128                 }
129             });
130         }
131     },
132     onNodeTextChange: function(event) {
133         var toSet = event.meta.node.getText();
134         this.children().some(function(child) {
135             if(child.wlxmlNode.sameNode(event.meta.node)) {
136                 if(toSet === '') {
137                     toSet = utils.unicode.ZWS;
138                 }
139                 if(toSet !== child.getText()) {
140                     child.setText(toSet);
141                 }
142                 return true;
143             }
144         });
145     },
146
147     onStateChange: function(changes) {
148         if(_.isBoolean(changes.exposed) && !this.isSpan()) {
149             this._container().toggleClass('highlighted-element', changes.exposed);
150         }
151         if(_.isBoolean(changes.active) && !this.isSpan()) {
152             this._container().toggleClass('current-node-element', changes.active);
153         }
154     },
155
156     ///
157
158     isSpan: function() {
159         return this.wlxmlNode.getTagName() === 'span';
160     },
161     
162     containsBlock: function() {
163         return this.children()
164             .filter(function(child) {
165                 return child instanceof documentElement.DocumentNodeElement;
166             })
167             .some(function(child) {
168                 if(child.isBlock()) {
169                     return true;
170                 } else {
171                     return child.containsBlock();
172                 }
173             });
174     },
175
176     prepend: function(param) {
177         var element;
178         if(param instanceof documentElement.DocumentElement) {
179             element = param;
180         } else {
181             element = this.canvas.createElement(param);
182         }
183         this._container().prepend(element.dom);
184         this.refreshPath();
185         return element;
186     },
187
188     childIndex: function(child) {
189         var children = this.children(),
190             toret = null;
191         children.forEach(function(c, idx) {
192             if(c.sameNode(child)) {
193                 toret = idx;
194                 return false;
195             }
196         });
197         return toret;
198     },
199
200     getWlxmlClass: function() {
201         var klass = this._container().attr('wlxml-class');
202         if(klass) {
203             return klass.replace(/-/g, '.');
204         }
205         return undefined;
206     },
207     setWlxmlClass: function(klass) {
208         if(klass === this.getWlxmlClass()) {
209             return;
210         }
211         if(klass) {
212             this._container().attr('wlxml-class', klass.replace(/\./g, '-'));
213         }
214         else {
215             this._container().removeAttr('wlxml-class');
216         }
217         this.refreshPath();
218     }
219 });
220
221
222 return generic;
223
224
225
226 });