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