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