appending node elements
[fnpeditor.git] / modules / documentCanvas / canvasManager.js
1 define([
2 'libs/jquery-1.9.1.min',
3 './canvasNode'
4 ], function($, canvasNode) {
5
6 'use strict';
7
8 var getCursorPosition = function() {
9     var selection = window.getSelection();
10     var anchorNode = $(selection.anchorNode);
11     var parent = anchorNode.parent();
12     return {
13         textNode: anchorNode,
14         textNodeOffset: selection.anchorOffset,
15         textNodeIndex: parent.contents().index(anchorNode),
16         parentNode: parent,
17         focusNode: $(selection.focusNode).parent(),
18         isAtEnd: selection.anchorOffset === anchorNode.text().length
19     };
20 };
21
22 var Manager = function(canvas, sandbox) {
23     this.canvas = canvas;
24     this.sandbox = sandbox;
25     this.shownAlready = false;
26     this.gridToggled = false;
27     this.scrollbarPosition = 0;
28     this.currentNode = null;
29     var manager = this;
30         
31     canvas.dom.find('#rng-module-documentCanvas-content').on('keyup', function() {
32         manager.sandbox.publish('contentChanged');
33     });
34
35     canvas.dom.on('mouseover', '[wlxml-tag]', function(e) {
36         e.stopPropagation();
37         manager.sandbox.publish('nodeHovered', canvasNode.create($(e.target)));
38     });
39     canvas.dom.on('mouseout', '[wlxml-tag]', function(e) {
40         e.stopPropagation();
41         manager.sandbox.publish('nodeBlured', canvasNode.create($(e.target)));
42     });
43     canvas.dom.on('click', '[wlxml-tag]', function(e) {
44         e.stopPropagation();
45         console.log('clicked node type: '+e.target.nodeType);
46         manager.selectNode(canvasNode.create($(e.target)));
47     });
48
49     canvas.dom.on('keyup', '#rng-module-documentCanvas-contentWrapper', function(e) {
50         var anchor = $(window.getSelection().anchorNode);
51         
52         if(anchor[0].nodeType === Node.TEXT_NODE)
53             anchor = anchor.parent();
54         if(!anchor.is('[wlxml-tag]'))
55             return;
56         manager.selectNode(canvasNode.create(anchor));
57     });
58     
59     canvas.dom.on('keydown', '#rng-module-documentCanvas-contentWrapper', function(e) {
60         if(e.which === 13) { 
61             manager.onEnterKey(e);
62         }
63         if(e.which === 8) {
64             manager.onBackspaceKey(e);
65         }
66     });
67               
68     canvas.dom.onShow = function() {
69         if(!manager.shownAlready) {
70             manager.shownAlready = true;
71             manager.selectFirstNode();
72         } else if(manager.currentNode) {
73             manager.movecaretToNode(manager.getNodeElement(manager.currentNode));
74             canvas.dom.find('#rng-module-documentCanvas-contentWrapper').scrollTop(manager.scrollbarPosition);
75         }
76     };
77     canvas.dom.onHide = function() {
78        manager.scrollbarPosition = canvas.dom.find('#rng-module-documentCanvas-contentWrapper').scrollTop();
79     };
80 };
81     
82 Manager.prototype.selectNode = function(cnode, options) {
83     options = options || {};
84     var nodeElement = this.getNodeElement(cnode);
85     
86     this.dimNode(cnode);
87     
88     this.canvas.dom.find('.rng-module-documentCanvas-currentNode').removeClass('rng-module-documentCanvas-currentNode');
89     nodeElement.addClass('rng-module-documentCanvas-currentNode');
90     
91     if(options.movecaret) {
92         this.movecaretToNode(nodeElement, options.movecaret);
93     }
94     
95     this.currentNode = cnode;
96     this.sandbox.publish('nodeSelected', cnode);
97 };
98
99 Manager.prototype.insertNewNode = function(wlxmlTag, wlxmlClass) {
100     var selection = window.getSelection(),
101         $anchorNode = $(selection.anchorNode),
102         $focusNode = $(selection.focusNode);
103         
104         
105     if(!selection.isCollapsed && $anchorNode.parent()[0] === $focusNode.parent()[0]) {
106         var textNodeIdx,
107             parent = $anchorNode.parent(),
108             parentContents = parent.contents(),
109             offsetStart = selection.anchorOffset,
110             offsetEnd = selection.focusOffset;
111         
112         if(selection.anchorNode === selection.focusNode) {
113             if(offsetStart > offsetEnd) {
114                 var tmp = offsetStart;
115                 offsetStart = offsetEnd;
116                 offsetEnd = tmp;
117             }
118             textNodeIdx = parentContents.index($anchorNode);
119         } else {
120             if(parentContents.index($anchorNode) > parentContents.index($focusNode)) {
121                 offsetStart = selection.focusOffset;
122                 offsetEnd = selection.anchorOffset;
123             }
124             textNodeIdx = [parentContents.index($anchorNode), parentContents.index($focusNode)];
125         }
126         
127         var wrapper = canvasNode.create({tag: wlxmlTag, klass: wlxmlClass});
128         this.canvas.nodeWrap({inside: canvasNode.create(parent),
129                               _with: wrapper,
130                               offsetStart: offsetStart,
131                               offsetEnd: offsetEnd,
132                               textNodeIdx: textNodeIdx
133                             });
134         this.selectNode(wrapper, {movecaret: 'end'});
135     }
136     
137     
138 };
139
140 Manager.prototype.getNodeElement = function(cnode) {
141     return this.canvas.dom.find('#'+cnode.getId());
142 };
143
144 Manager.prototype.highlightNode = function(cnode) {
145     var nodeElement = this.getNodeElement(cnode);
146     if(!this.gridToggled) {
147         nodeElement.addClass('rng-common-hoveredNode');
148         var label = nodeElement.attr('wlxml-tag');
149         if(nodeElement.attr('wlxml-class'))
150             label += ' / ' + nodeElement.attr('wlxml-class');
151         var tag = $('<div>').addClass('rng-module-documentCanvas-hoveredNodeTag').text(label);
152         nodeElement.append(tag);
153     }
154 };
155
156 Manager.prototype.dimNode = function(cnode) {
157     var nodeElement = this.getNodeElement(cnode);
158     if(!this.gridToggled) {
159         nodeElement.removeClass('rng-common-hoveredNode');
160         nodeElement.find('.rng-module-documentCanvas-hoveredNodeTag').remove();
161     }
162 };
163
164 Manager.prototype.selectFirstNode = function() {
165     var firstNodeWithText = this.canvas.dom.find('[wlxml-tag]').filter(function() {
166         return $(this).clone().children().remove().end().text().trim() !== '';
167     }).first();
168     var node;
169     if(firstNodeWithText.length)
170         node = $(firstNodeWithText[0]);
171     else {
172         node = this.canvas.dom.find('[wlxml-class|="p"]');
173     }
174     this.selectNode(canvasNode.create(node), {movecaret: true});
175 };
176
177 Manager.prototype.movecaretToNode = function(nodeElement, where) {
178     if(!nodeElement.length)
179         return;
180     var range = document.createRange();
181     range.selectNodeContents(nodeElement[0]);
182     
183     var collapseArg = true;
184     if(where === 'end')
185         collapseArg = false;
186     range.collapse(collapseArg);
187     var selection = document.getSelection();
188     selection.removeAllRanges();
189     selection.addRange(range);
190 };
191
192 Manager.prototype.toggleGrid =  function(toggle) {
193     this.canvas.dom.find('[wlxml-tag]').toggleClass('rng-common-hoveredNode', toggle);
194     this.gridToggled = toggle;
195 };
196
197 Manager.prototype.onEnterKey = function(e) {
198     e.preventDefault();
199     var pos = getCursorPosition();
200     var contextNode = this.canvas.getNodeById(pos.parentNode.attr('id'));
201     var newNode;
202
203     if(pos.isAtEnd) {
204         newNode = canvasNode.create({tag: pos.parentNode.attr('wlxml-tag'), klass: pos.parentNode.attr('wlxml-class')});
205         this.canvas.nodeInsertAfter({node: newNode, after: this.canvas.getNodeById(pos.parentNode.attr('id'))});
206     } else {
207         newNode = this.canvas.nodeSplit({node: contextNode, textNodeIdx: pos.textNodeIndex, offset: pos.textNodeOffset});
208     }
209     if(newNode)
210         this.selectNode(newNode, {movecaret: true});
211     this.sandbox.publish('contentChanged');
212 };
213
214 Manager.prototype.onBackspaceKey = function(e) {
215     var pos = getCursorPosition();
216     var len = pos.textNode.text().length;
217     if(len === 1) {
218         // Prevent deleting node by browser after last character removed;
219         e.preventDefault();
220         pos.parentNode.text('');
221     }
222     if(len === 0) {
223         e.preventDefault();
224         var toRemove = canvasNode.create(pos.textNode);
225         var prevNode = this.canvas.getPrecedingNode({node:toRemove});
226         this.canvas.nodeRemove({node: toRemove}); // jesli nie ma tekstu, to anchor nie jest tex nodem
227         this.selectNode(prevNode, {movecaret: 'end'});
228     }
229 };
230
231 Manager.prototype.command = function(command, meta) {
232     var pos = getCursorPosition();
233     
234     if(command === 'createList') {
235         var node = canvasNode.create(pos.parentNode);
236         if(window.getSelection().getRangeAt(0).collapsed && this.canvas.nodeInsideList({node: node})) {
237             this.canvas.listRemove({pointer: node});
238             this.selectNode(node, {movecaret: 'end'});
239             this.sandbox.publish('contentChanged');
240         }
241         else {
242             //if(!this.canvas.nodeInsideList({node: node})) {
243                 this.canvas.listCreate({start: node, end: canvasNode.create(pos.focusNode), type: meta});
244                 this.selectNode(node, {movecaret: 'end'});
245                 this.sandbox.publish('contentChanged');
246             //}
247         }
248     } else if(command === 'unwrap-node') {
249         this.canvas.nodeUnwrap({node: canvasNode.create(pos.parentNode)});
250         this.sandbox.publish('contentChanged');
251     }
252
253 };
254
255
256 return Manager;
257     
258 });