pre d: setattr, settext, split as ContextTransformations; detach in old format; pre...
[fnpeditor.git] / src / editor / modules / documentCanvas / canvas / keyboard.js
1 define([
2 'modules/documentCanvas/canvas/documentElement',
3 'modules/documentCanvas/canvas/utils'
4 ], function(documentElement, utils) {
5     
6 'use strict';
7
8
9 var KEYS = {
10     ENTER: 13,
11     ARROW_LEFT: 37,
12     ARROW_UP: 38,
13     ARROW_RIGHT: 39,
14     ARROW_DOWN: 40,
15     BACKSPACE: 8,
16     DELETE: 46,
17     X: 88
18 };
19
20 var handleKey = function(event, canvas) {
21     handlers.some(function(handler) {
22         if(handles(handler, event) && handler[event.type]) {
23             handler[event.type](event, canvas);
24             return true;
25         }
26     });
27 };
28
29 var handles = function(handler, event) {
30     if(handler.key === event.which)
31         return true;
32     if(handler.keys && handler.keys.indexOf(event.which) !== -1)
33         return true;
34     return false;
35 };
36
37 var handlers = [];
38
39
40 handlers.push({key: KEYS.ENTER,
41     keydown: function(event, canvas) {
42         event.preventDefault();
43         var cursor = canvas.getCursor(),
44             position = cursor.getPosition(),
45             element = position.element;
46
47         if(Object.keys(cursor.getPosition()).length === 0) {
48             var currentElement = canvas.getCurrentNodeElement();
49             if(currentElement) {
50                 var added = currentElement.data('wlxmlNode').after({
51                     tag: currentElement.getWlxmlTag() || 'div',
52                     attrs: {'class': currentElement.getWlxmlClass() || 'p'}
53                 });
54                 added.append({text:''});
55                 canvas.setCurrentElement(utils.findCanvasElement(added), {caretTo: 'start'});
56             }
57             return;
58         }
59
60         if(!cursor.isSelecting()) {
61             if(event.ctrlKey) {
62                 if(element instanceof documentElement.DocumentTextElement) {
63                     element = element.parent();
64                 }
65                 var added = element.data('wlxmlNode').after(
66                     {tagName: element.getWlxmlTag() || 'div', attrs: {'class': element.getWlxmlClass() || 'p'}}
67                 );
68                 added.append({text: ''});
69                 canvas.setCurrentElement(utils.findCanvasElement(added), {caretTo: 'start'});
70
71             } else {
72
73                 if(!(element.parent().parent())) {
74                     return false; // top level element is unsplittable
75                 }
76
77                 //var nodes = position.element.data('wlxmlNode').split({offset: position.offset}),
78                 var nodes = position.element.data('wlxmlNode').transform('split', {offset: position.offset}),
79                     newEmpty,
80                     goto,
81                     gotoOptions;
82
83                 if(position.offsetAtBeginning)
84                     newEmpty = nodes.first;
85                 else if(position.offsetAtEnd)
86                     newEmpty = nodes.second;
87                 
88                 if(newEmpty) {
89                     //goto = newEmpty.append({text: ''});
90                     gotoOptions = {};
91                 } else {
92                     goto = nodes.second;
93                     gotoOptions = {caretTo: 'start'};
94                 }
95
96                 canvas.setCurrentElement(utils.findCanvasElement(goto), gotoOptions);
97             }
98         }
99     }
100 });
101
102 handlers.push({keys: [KEYS.ARROW_UP, KEYS.ARROW_DOWN, KEYS.ARROW_LEFT, KEYS.ARROW_RIGHT],
103     keydown: function(event, canvas) {
104         var position = canvas.getCursor().getPosition(),
105             element = position.element;
106         if(element && (element instanceof documentElement.DocumentTextElement) && element.isEmpty()) {
107             var direction, caretTo;
108             if(event.which === KEYS.ARROW_LEFT  || event.which === KEYS.ARROW_UP) {
109                 direction = 'above';
110                 caretTo = 'end';
111             } else {
112                 direction = 'below';
113                 caretTo = 'start';
114             }
115             var el = canvas.getDocumentElement(utils.nearestInDocumentOrder('[document-text-element]', direction, element.dom()[0]));
116             canvas.setCurrentElement(el, {caretTo: caretTo});
117         }
118     },
119     keyup: function(event, canvas) {
120         var element = canvas.getCursor().getPosition().element,
121             caretTo = false;
122         if(!element) {
123             // Chrome hack
124             var direction;
125             if(event.which === KEYS.ARROW_LEFT  || event.which === KEYS.ARROW_UP) {
126                 direction = 'above';
127                 caretTo = 'end';
128             } else {
129                 direction = 'below';
130                 caretTo = 'start';
131             }
132             element = canvas.getDocumentElement(utils.nearestInDocumentOrder('[document-text-element]:visible', direction, window.getSelection().focusNode));
133         }
134         canvas.setCurrentElement(element, {caretTo: caretTo});
135     }
136 });
137
138
139 var selectsWholeTextElement = function(cursor) {
140     if(cursor.isSelecting() && cursor.getSelectionStart().offsetAtBeginning && cursor.getSelectionEnd().offsetAtEnd)
141         return true;
142     return false;
143 }
144
145 handlers.push({key: KEYS.X,
146     keydown: function(event, canvas) {
147         if(event.ctrlKey && selectsWholeTextElement(canvas.getCursor()))
148             event.preventDefault();
149     }
150 });
151
152 handlers.push({keys: [KEYS.BACKSPACE, KEYS.DELETE],
153     keydown: function(event, canvas) {
154         var cursor = canvas.getCursor(),
155             position = canvas.getCursor().getPosition(),
156             element = position.element;
157
158         if(cursor.isSelecting() && !cursor.isSelectingWithinElement()) {
159             event.preventDefault();
160             return;
161         }
162             
163         var cursorAtOperationEdge = position.offsetAtBeginning;
164         if(event.which === KEYS.DELETE) {
165             cursorAtOperationEdge = position.offsetAtEnd;
166         }
167
168         var willDeleteWholeText = function() {
169             return element.getText().length === 1 || selectsWholeTextElement(cursor);
170         }
171
172         if(willDeleteWholeText()) {
173             event.preventDefault();
174             element.setText('');
175         }
176         else if(element.isEmpty()) {
177
178             var direction = 'above',
179                 caretTo = 'end';
180                 
181             if(event.which === KEYS.DELETE) {
182                 direction = 'below';
183                 caretTo = 'start';
184             }
185
186             event.preventDefault();
187
188             var parent = element.parent(),
189                 grandParent = parent ? parent.parent() : null,
190                 goto;
191             if(parent.children().length === 1 && parent.children()[0].sameNode(element)) {
192                 if(grandParent && grandParent.children().length === 1) {
193                     goto = grandParent.data('wlxmlNode').append({text: ''});
194                 } else {
195                     goto = element.getNearestTextElement(direction);
196                 }
197                 parent.data('wlxmlNode').detach();
198             } else {
199                 goto = element.getNearestTextElement(direction);
200                 element.data('wlxmlNode').detach();
201             }
202             canvas.setCurrentElement(goto, {caretTo: caretTo});
203             canvas.publisher('contentChanged');
204         }
205         else if(cursorAtOperationEdge) {
206             // todo
207             event.preventDefault();
208         }
209     }
210 });
211
212 return {
213     handleKey: handleKey
214 };
215
216 });