canvas manager wip: experiments
[fnpeditor.git] / modules / documentCanvas / canvasManager.js
1 define([\r
2 'libs/jquery-1.9.1.min',\r
3 './wlxmlNode'\r
4 ], function($, wlxmlNode) {\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         isAtEnd: selection.anchorOffset === anchorNode.text().length\r
18     }\r
19 };\r
20 \r
21 var Manager = function(canvas, sandbox) {\r
22     this.canvas = canvas;\r
23     this.sandbox = sandbox;\r
24     this.shownAlready = false;\r
25     this.gridToggled = false;\r
26     this.scrollbarPosition = 0;\r
27     this.currentNode = null;\r
28     var manager = this;\r
29         \r
30     canvas.dom.find('#rng-module-documentCanvas-content').on('keyup', function() {\r
31         manager.sandbox.publish('contentChanged');\r
32     });\r
33 \r
34     canvas.dom.on('mouseover', '[wlxml-tag]', function(e) {\r
35         e.stopPropagation();\r
36         manager.sandbox.publish('nodeHovered', new wlxmlNode.Node($(e.target)));\r
37     });\r
38     canvas.dom.on('mouseout', '[wlxml-tag]', function(e) {\r
39         e.stopPropagation();\r
40         manager.sandbox.publish('nodeBlured', new wlxmlNode.Node($(e.target)));\r
41     });\r
42     canvas.dom.on('click', '[wlxml-tag]', function(e) {\r
43         e.stopPropagation();\r
44         console.log('clicked node type: '+e.target.nodeType);\r
45         manager.selectNode(new wlxmlNode.Node($(e.target)));\r
46     });\r
47 \r
48     canvas.dom.on('keyup', '#rng-module-documentCanvas-contentWrapper', function(e) {\r
49         var anchor = $(window.getSelection().anchorNode);\r
50         \r
51         if(anchor[0].nodeType === Node.TEXT_NODE)\r
52             anchor = anchor.parent();\r
53         if(!anchor.is('[wlxml-tag]'))\r
54             return;\r
55         manager.selectNode(new wlxmlNode.Node(anchor));\r
56     });\r
57     \r
58     canvas.dom.on('keydown', '#rng-module-documentCanvas-contentWrapper', function(e) {\r
59         if(e.which === 13) { \r
60             manager.onEnterKey(e);\r
61         }\r
62         if(e.which === 8) {\r
63             manager.onBackspaceKey(e);\r
64         }\r
65     });\r
66               \r
67     canvas.dom.onShow = function() {\r
68         if(!manager.shownAlready) {\r
69             manager.shownAlready = true;\r
70             manager.selectFirstNode();\r
71         } else if(manager.currentNode) {\r
72             manager.movecaretToNode(manager.getNodeElement(manager.currentNode));\r
73             canvas.dom.find('#rng-module-documentCanvas-contentWrapper').scrollTop(manager.scrollbarPosition);\r
74         }\r
75     };\r
76     canvas.dom.onHide = function() {\r
77        manager.scrollbarPosition = canvas.dom.find('#rng-module-documentCanvas-contentWrapper').scrollTop();\r
78     }\r
79 };\r
80     \r
81 Manager.prototype.selectNode = function(wlxmlNode, options) {\r
82     options = options || {};\r
83     var nodeElement = this.getNodeElement(wlxmlNode)\r
84     \r
85     this.dimNode(wlxmlNode);\r
86     \r
87     this.canvas.dom.find('.rng-module-documentCanvas-currentNode').removeClass('rng-module-documentCanvas-currentNode');\r
88     nodeElement.addClass('rng-module-documentCanvas-currentNode');\r
89     \r
90     if(options.movecaret) {\r
91         this.movecaretToNode(nodeElement, options.movecaret);\r
92     }\r
93     \r
94     this.currentNode = wlxmlNode;\r
95     this.sandbox.publish('nodeSelected', wlxmlNode);\r
96 };\r
97 \r
98 Manager.prototype.getNodeElement = function(wlxmlNode) {\r
99     return this.canvas.dom.find('#'+wlxmlNode.id);\r
100 };\r
101 \r
102 Manager.prototype.highlightNode = function(wlxmlNode) {\r
103     var nodeElement = this.getNodeElement(wlxmlNode);\r
104     if(!this.gridToggled) {\r
105         nodeElement.addClass('rng-common-hoveredNode');\r
106         var label = nodeElement.attr('wlxml-tag');\r
107         if(nodeElement.attr('wlxml-class'))\r
108             label += ' / ' + nodeElement.attr('wlxml-class');\r
109         var tag = $('<div>').addClass('rng-module-documentCanvas-hoveredNodeTag').text(label);\r
110         nodeElement.append(tag);\r
111     }\r
112 };\r
113 \r
114 Manager.prototype.dimNode = function(wlxmlNode) {\r
115     var nodeElement = this.getNodeElement(wlxmlNode);\r
116     if(!this.gridToggled) {\r
117         nodeElement.removeClass('rng-common-hoveredNode');\r
118         nodeElement.find('.rng-module-documentCanvas-hoveredNodeTag').remove();\r
119     }\r
120 };\r
121 \r
122 Manager.prototype.selectFirstNode = function() {\r
123     var firstNodeWithText = this.canvas.dom.find('[wlxml-tag]').filter(function() {\r
124         return $(this).clone().children().remove().end().text().trim() !== '';\r
125     }).first();\r
126     var node;\r
127     if(firstNodeWithText.length)\r
128         node = $(firstNodeWithText[0])\r
129     else {\r
130         node = this.canvas.dom.find('[wlxml-class|="p"]')\r
131     }\r
132     this.selectNode(new wlxmlNode.Node(node), {movecaret: true});\r
133 };\r
134 \r
135 Manager.prototype.movecaretToNode = function(nodeElement, where) {\r
136     var range = document.createRange();\r
137     range.selectNodeContents(nodeElement[0]);\r
138     \r
139     var collapseArg = true;\r
140     if(where === 'end')\r
141         collapseArg = false;\r
142     range.collapse(collapseArg);\r
143     var selection = document.getSelection();\r
144     selection.removeAllRanges()\r
145     selection.addRange(range);\r
146 };\r
147 \r
148 Manager.prototype.toggleGrid =  function(toggle) {\r
149     this.canvas.dom.find('[wlxml-tag]').toggleClass('rng-common-hoveredNode', toggle);\r
150     this.gridToggled = toggle;\r
151 };\r
152 \r
153 Manager.prototype.onEnterKey = function(e) {\r
154     e.preventDefault();\r
155     var pos = getCursorPosition();\r
156     var insertedNode;\r
157     if(pos.isAtEnd) {\r
158         insertedNode = this.canvas.insertNode({place: 'after', context: {id: pos.parentNode.attr('id')}, tag: pos.parentNode.attr('wlxml-tag'), klass: pos.parentNode.attr('wlxml-class')});\r
159     } else {\r
160         insertedNode = this.canvas.splitNode({node: {id: pos.parentNode.attr('id')}, textNodeIdx: pos.textNodeIndex, offset: pos.textNodeOffset});\r
161     }\r
162     if(insertedNode.length)\r
163         this.selectNode(new wlxmlNode.Node(insertedNode), {movecaret: true});\r
164     this.sandbox.publish('contentChanged');\r
165 };\r
166 \r
167 Manager.prototype.onBackspaceKey = function(e) {\r
168     var pos = getCursorPosition();\r
169     var len = pos.textNode.text().length;\r
170     if(len === 1) {\r
171         // Prevent deleting node by browser after last character removed;\r
172         e.preventDefault();\r
173         pos.parentNode.text('');\r
174     }\r
175     if(len === 0) {\r
176         e.preventDefault();\r
177         var toRemove = new wlxmlNode.Node(pos.textNode);\r
178         var prevNode = this.canvas.getPreviousNode({node:toRemove});\r
179         this.canvas.removeNode({node: toRemove}); // jesli nie ma tekstu, to anchor nie jest tex nodem\r
180         this.selectNode(prevNode, {movecaret: 'end'});\r
181     }\r
182 }\r
183 \r
184 \r
185 \r
186 return Manager;\r
187     \r
188 });