b483937a122a3df7a5b95ef23bfe6b25769cbdb9
[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.doc().dom().find('#rng-module-documentCanvas-content').on('keyup', function() {
32         manager.sandbox.publish('contentChanged');
33     });
34
35     canvas.doc().dom().on('mouseover', '[wlxml-tag]', function(e) {
36         e.stopPropagation();
37         manager.sandbox.publish('nodeHovered', canvasNode.create($(e.target)));
38     });
39     canvas.doc().dom().on('mouseout', '[wlxml-tag]', function(e) {
40         e.stopPropagation();
41         manager.sandbox.publish('nodeBlured', canvasNode.create($(e.target)));
42     });
43     canvas.doc().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.doc().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.doc().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.doc().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.doc().dom().find('#rng-module-documentCanvas-contentWrapper').scrollTop(manager.scrollbarPosition);
75         }
76     };
77     canvas.doc().dom().onHide = function() {
78        manager.scrollbarPosition = canvas.doc().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.doc().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.getNodeElement = function(cnode) {
100     return this.canvas.doc().dom().find('#'+cnode.getId());
101 };
102
103 Manager.prototype.highlightNode = function(cnode) {
104     var nodeElement = this.getNodeElement(cnode);
105     if(!this.gridToggled) {
106         nodeElement.addClass('rng-common-hoveredNode');
107         var label = nodeElement.attr('wlxml-tag');
108         if(nodeElement.attr('wlxml-class'))
109             label += ' / ' + nodeElement.attr('wlxml-class');
110         var tag = $('<div>').addClass('rng-module-documentCanvas-hoveredNodeTag').text(label);
111         nodeElement.append(tag);
112     }
113 };
114
115 Manager.prototype.dimNode = function(cnode) {
116     var nodeElement = this.getNodeElement(cnode);
117     if(!this.gridToggled) {
118         nodeElement.removeClass('rng-common-hoveredNode');
119         nodeElement.find('.rng-module-documentCanvas-hoveredNodeTag').remove();
120     }
121 };
122
123 Manager.prototype.selectFirstNode = function() {
124     var firstNodeWithText = this.canvas.doc().dom().find('[wlxml-tag]').filter(function() {
125         return $(this).clone().children().remove().end().text().trim() !== '';
126     }).first();
127     var node;
128     if(firstNodeWithText.length)
129         node = $(firstNodeWithText[0]);
130     else {
131         node = this.canvas.doc().dom().find('[wlxml-class|="p"]');
132     }
133     this.selectNode(canvasNode.create(node), {movecaret: true});
134 };
135
136 Manager.prototype.movecaretToNode = function(nodeElement, where) {
137     if(!nodeElement.length)
138         return;
139     var range = document.createRange();
140     range.selectNodeContents(nodeElement[0]);
141     
142     var collapseArg = true;
143     if(where === 'end')
144         collapseArg = false;
145     range.collapse(collapseArg);
146     var selection = document.getSelection();
147     selection.removeAllRanges();
148     selection.addRange(range);
149 };
150
151 Manager.prototype.onEnterKey = function(e) {
152     e.preventDefault();
153     var pos = getCursorPosition();
154     var contextNode = this.canvas.getNodeById(pos.parentNode.attr('id'));
155     var newNode;
156
157     if(pos.isAtEnd) {
158         newNode = canvasNode.create({tag: pos.parentNode.attr('wlxml-tag'), klass: pos.parentNode.attr('wlxml-class')});
159         this.canvas.nodeInsertAfter({node: newNode, after: this.canvas.getNodeById(pos.parentNode.attr('id'))});
160     } else {
161         newNode = this.canvas.nodeSplit({node: contextNode, textNodeIdx: pos.textNodeIndex, offset: pos.textNodeOffset});
162     }
163     if(newNode)
164         this.selectNode(newNode, {movecaret: true});
165     this.sandbox.publish('contentChanged');
166 };
167
168 Manager.prototype.onBackspaceKey = function(e) {
169     var pos = getCursorPosition();
170     var len = pos.textNode.text().length;
171     if(len === 1) {
172         // Prevent deleting node by browser after last character removed;
173         e.preventDefault();
174         pos.parentNode.text('');
175     }
176     if(len === 0) {
177         e.preventDefault();
178         var toRemove = canvasNode.create(pos.textNode);
179         var prevNode = this.canvas.getPrecedingNode({node:toRemove});
180         this.canvas.nodeRemove({node: toRemove}); // jesli nie ma tekstu, to anchor nie jest tex nodem
181         this.selectNode(prevNode, {movecaret: 'end'});
182     }
183 };
184
185 Manager.prototype.toggleList = function(toggle) {
186     var selection  = window.getSelection(),
187         node1 = $(selection.anchorNode).parent()[0],
188         node2 = $(selection.focusNode).parent()[0],
189         element1 = this.canvas.getDocumentElement(node1),
190         element2 = this.canvas.getDocumentElement(node2);
191
192 };
193
194 Manager.prototype.command = function(command, params) {
195     var selection  = window.getSelection(),
196         element1 = this.canvas.getDocumentElement(selection.anchorNode),
197         element2 = this.canvas.getDocumentElement(selection.focusNode),
198         parent1 = element1.parent(),
199         parent2 = element2.parent();
200
201     if(command === 'unwrap-node') {
202         // this.canvas.nodeUnwrap({node: canvasNode.create(pos.parentNode)});
203         // this.sandbox.publish('contentChanged');
204         if(this.canvas.list.areItemsOfTheSameList({element1: parent1, element2: parent2})) {
205             this.canvas.list.extractItems({element1: parent1, element2: parent2});
206         } else if (!selection.collapsed) {
207             element1.unwrap();
208         }
209     } else if(command === 'wrap-node') {
210         if(this.canvas.list.areItemsOfTheSameList({element1: parent1, element2: parent2})) {
211             this.canvas.list.create({element1: parent1, element2: parent2});
212         }
213     } else if(command === 'toggle-list') {
214         if(params.toggle) {
215             this.canvas.list.create({element1: parent1, element2: parent2});
216         } else {
217             if(this.canvas.list.areItemsOfTheSameList({element1: parent1, element2: parent2})) {
218                 this.canvas.list.extractItems({element1: parent1, element2: parent2, merge: false});
219             } 
220         }
221     } else if(command == 'toggle-grid') {
222         this.canvas.doc().dom().find('[wlxml-tag]').toggleClass('rng-common-hoveredNode', params.toggle);
223         this.gridToggled = params.toggle;
224     } else if(command == 'newNodeRequested') {
225         if(!selection.isCollapsed && element1.parent().sameNode(element2.parent())) {
226             var parent = element1.parent(),
227                 offsetStart = selection.anchorOffset,
228                 offsetEnd = selection.focusOffset;
229
230             if(element1.sameNode(element2)) {
231                 element1.wrapWithNodeElement({tag: params.wlxmlTag, klass: params.wlxmlClass, start: offsetStart, end: offsetEnd});
232             }
233             else {
234                 if(parent.childIndex(element1) > parent.childIndex(element2)) {
235                     var tmp = offsetStart;
236                     offsetStart = offsetEnd;
237                     offsetEnd = tmp;
238                 }
239                 this.canvas.wrapText({
240                     inside: parent,
241                     _with: {tag: params.wlxmlTag, klass: params.wlxmlClass},
242                     offsetStart: offsetStart,
243                     offsetEnd: offsetEnd,
244                     textNodeIdx: [parent.childIndex(element1), parent.childIndex(element2)]
245                 });
246             }
247         }
248     }
249 };
250
251
252 return Manager;
253     
254 });