a411a37e77f80663f37153d41ed1538b394b8530
[fnpeditor.git] / src / editor / modules / documentCanvas / commands.js
1 define([
2 './canvas/utils',
3 'views/dialog/dialog',
4 'fnpjs/datetime'
5 ], function(utils, Dialog, datetime) {
6     
7 'use strict';
8 /* globals gettext */
9
10
11 var gridToggled = false;
12
13 var commands = {
14     _cmds: {},
15     register: function(name, command) {
16         this._cmds[name] = command;
17     },
18
19     run: function(name, params, canvas, user) {
20         return this._cmds[name](canvas, params, user);
21     }
22 };
23
24 commands.register('undo', function(canvas) {
25     var doc = canvas.wlxmlDocument;
26
27     doc.undo();
28 });
29
30 commands.register('redo', function(canvas) {
31     var doc = canvas.wlxmlDocument;
32
33     doc.redo();
34 });
35
36 commands.register('remove-node', function(canvas) {
37     canvas.getCurrentNodeElement().wlxmlNode.detach();
38 });
39
40 commands.register('unwrap-node', function(canvas) {
41     var cursor = canvas.getCursor(),
42         selectionStart = cursor.getSelectionStart(),
43         selectionEnd = cursor.getSelectionEnd(),
44         parent1 = selectionStart.element.parent() || undefined,
45         parent2 = selectionEnd.element.parent() || undefined;
46
47     var selectionAnchor = cursor.getSelectionAnchor(),
48         node1 = parent1.wlxmlNode,
49         node2 = parent2.wlxmlNode,
50         doc = node1.document;
51     if(doc.areItemsOfSameList({node1: node1, node2: node2})) {
52         doc.extractItems({item1: node1, item2: node2});
53         canvas.setCurrentElement(selectionAnchor.element, {caretTo: selectionAnchor.offset});
54     } else if(!cursor.isSelecting()) {
55         var nodeToUnwrap = cursor.getPosition().element.wlxmlNode,
56             parentNode = nodeToUnwrap.unwrap();
57         if(parentNode) {
58             canvas.setCurrentElement(utils.findCanvasElement(parentNode));
59         }
60     }
61 });
62
63 commands.register('wrap-node', function(canvas) {
64     var cursor = canvas.getCursor(),
65         selectionStart = cursor.getSelectionStart(),
66         selectionEnd = cursor.getSelectionEnd(),
67         parent1 = selectionStart.element.parent() || undefined,
68         parent2 = selectionEnd.element.parent() || undefined;
69
70     var node1 = parent1.wlxmlNode,
71         node2 = parent2.wlxmlNode,
72         doc = node1.document;
73
74     if(doc.areItemsOfSameList({node1: node1, node2: node2})) {
75         doc.createList({node1: node1, node2: node2});
76     }
77 });
78
79 commands.register('list', function(canvas, params) {
80     void(params);
81     var cursor = canvas.getCursor(),
82         selectionStart = cursor.getSelectionStart(),
83         selectionEnd = cursor.getSelectionEnd(),
84         parent1 = selectionStart.element.parent() || undefined,
85         parent2 = selectionEnd.element.parent() || undefined;
86
87     var selectionFocus = cursor.getSelectionFocus();
88
89     if(selectionStart.element.isInsideList() || selectionEnd.element.isInsideList()) {
90         return;
91     }
92
93     var node1 = parent1.wlxmlNode,
94         node2 = parent2.wlxmlNode,
95         doc = node1.document;
96     
97     doc.createList({node1: node1, node2: node2});
98     canvas.setCurrentElement(selectionFocus.element, {caretTo: selectionFocus.offset});
99 });
100
101 commands.register('toggle-grid', function(canvas, params) {
102     canvas.doc().dom().parent().toggleClass('grid-on', params.toggle);
103     gridToggled = params.toggle;
104 });
105
106 commands.register('newNodeRequested', function(canvas, params, user) {
107     var cursor = canvas.getCursor(),
108         selectionStart = cursor.getSelectionStart(),
109         selectionEnd = cursor.getSelectionEnd(),
110         wlxmlNode, caretTo, wrapperCanvasElement;
111
112     var insertNode = function(insertion, callback) {
113         var doc = canvas.wlxmlDocument,
114             metadata, creator, dialog;
115
116         var execCallback = function(node) {
117             if(callback) {
118                 callback(node);
119             }
120         };
121
122         if(params.wlxmlTag === 'aside' && params.wlxmlClass === 'comment') {
123             doc.transaction(function() {
124                 var node = insertion();
125                 if(user) {
126                     creator = user.name;
127                     if(user.email) {
128                         creator += ' (' + user.email + ')';
129                     }
130                 } else {
131                     creator = 'anonymous';
132                 }
133
134                 metadata = node.getMetadata();
135                 metadata.add({key: 'creator', value: creator});
136                 metadata.add({key: 'date', value: datetime.currentStrfmt()});
137                 return node;
138             }, {
139                 success: execCallback
140             });
141         } else if(params.wlxmlClass === 'link') {
142             dialog = Dialog.create({
143                 title: gettext('Create link'),
144                 executeButtonText: gettext('Apply'),
145                 cancelButtonText: gettext('Cancel'),
146                 fields: [
147                     {label: gettext('Link'), name: 'href', type: 'input'}
148                 ]
149             });
150             dialog.on('execute', function(event) {
151                 doc.transaction(function() {
152                     var node = insertion();
153                     node.setAttr('href', event.formData.href);
154                     event.success();
155                     return node;
156                 }, {
157                     success: execCallback
158                 });
159             });
160             dialog.show();
161         } else {
162             doc.transaction(function() {
163                 return insertion();
164             }, {success: execCallback});
165         }
166     };
167
168     if(cursor.isSelecting()) {
169         if(cursor.isSelectingSiblings()) {
170             if(cursor.isSelectingWithinElement()) {
171                 wlxmlNode = selectionStart.element.wlxmlNode;
172                 caretTo = selectionStart.offset < selectionEnd.offset ? 'start' : 'end';
173
174                 insertNode(
175                     function() {
176                         return wlxmlNode.wrapWith({tagName: params.wlxmlTag, attrs: {'class': params.wlxmlClass}, start: selectionStart.offset, end: selectionEnd.offset});
177                     },
178                     function(wrapper) {
179                         wrapperCanvasElement = utils.findCanvasElement(wrapper);
180                         canvas.setCurrentElement(wrapperCanvasElement.children()[0], {caretTo: caretTo});
181                     }
182                 );
183             }
184             else {
185                 wlxmlNode = selectionStart.element.wlxmlNode.parent();
186                 caretTo = selectionStart.element.sameNode(cursor.getSelectionAnchor().element) ? 'end' : 'start';
187
188                 insertNode(
189                     function() {
190                         return wlxmlNode.wrapText({
191                             _with: {tagName: params.wlxmlTag, attrs: {'class': params.wlxmlClass}},
192                             offsetStart: selectionStart.offset,
193                             offsetEnd: selectionEnd.offset,
194                             textNodeIdx: [wlxmlNode.indexOf(selectionStart.element.wlxmlNode), wlxmlNode.indexOf(selectionEnd.element.wlxmlNode)] //parent.childIndex(selectionEnd.element)]
195                         });
196                     },
197                     function(wrapper) {
198                         wrapperCanvasElement = utils.findCanvasElement(wrapper);
199                         canvas.setCurrentElement(wrapperCanvasElement.children()[caretTo === 0 ? 0 : wrapperCanvasElement.children().length - 1], {caretTo: caretTo});
200                     }
201                 );
202             }
203         } else {
204             var node1 = selectionStart.element.wlxmlNode,
205                 node2 = selectionEnd.element.wlxmlNode,
206                 siblingParents = canvas.wlxmlDocument.getSiblingParents({node1: node1, node2: node2});
207
208             if(siblingParents) {
209                 insertNode(
210                     function() {
211                         return canvas.wlxmlDocument.wrapNodes({
212                             node1: siblingParents.node1,
213                             node2: siblingParents.node2,
214                             _with: {tagName: params.wlxmlTag, attrs: {'class': params.wlxmlClass}}
215                         });
216                     }
217                 );
218             }
219         }
220     } else if(canvas.getCurrentNodeElement()) {
221         wlxmlNode = canvas.getCurrentNodeElement().wlxmlNode;
222
223         var linkFound = [wlxmlNode].concat(wlxmlNode.parents()).some(function(node) {
224             if(node.getClass() === 'link') {
225                 var dialog = Dialog.create({
226                     title: gettext('Edit link'),
227                     executeButtonText: gettext('Apply'),
228                     cancelButtonText: gettext('Cancel'),
229                     fields: [
230                         {label: gettext('Link'), name: 'href', type: 'input', initialValue: node.getAttr('href')},
231                     ]
232                 });
233                 dialog.on('execute', function(event) {
234                     canvas.wlxmlDocument.transaction(function() {
235                         node.setAttr('href', event.formData.href);
236                         event.success();
237                     });
238                     canvas.wlxmlDocument.endTransaction();
239                 });
240                 dialog.show();
241                 return true;
242             }
243         });
244         if(linkFound) {
245             return;
246         }
247
248         if(params.ctrlKey) {
249             insertNode(
250                 function() {
251                     return wlxmlNode.wrapWith({tagName: params.wlxmlTag, attrs: {'class': params.wlxmlClass}});
252                 },
253                 function(wrapper) {
254                     canvas.setCurrentElement(utils.findCanvasElement(wrapper));
255                 }
256             );
257         } else {
258             insertNode(
259                 function() {
260                     var node = wlxmlNode.after({tagName: params.wlxmlTag, attrs: {'class': params.wlxmlClass}});
261                     node.append({text:''});
262                     return node;
263                 }, function(wrapper) {
264                     canvas.setCurrentElement(utils.findCanvasElement(wrapper));
265                 }
266             );
267         }
268     }
269 });
270
271 commands.register('footnote', function(canvas, params) {
272     void(params);
273     var cursor = canvas.getCursor(),
274         position = cursor.getPosition(),
275         asideNode, asideElement, node;
276         
277
278     if(cursor.isSelectingWithinElement()) {
279         asideNode = position.element.wlxmlNode.wrapWith({tagName: 'aside', attrs:{'class': 'footnote'}, start: cursor.getSelectionStart().offset, end: cursor.getSelectionEnd().offset});
280     } else {
281         node = position.element.wlxmlNode;
282         node.document.transaction(function() {
283             asideNode = node.divideWithElementNode({tagName: 'aside', attrs:{'class': 'footnote'}}, {offset: position.offset});
284             asideNode.append({text: ''});
285         });
286     }
287
288     asideElement = utils.findCanvasElement(asideNode);
289     asideElement.toggle(true);
290     canvas.setCurrentElement(asideElement);
291 });
292
293 commands.register('take-away-node', function(canvas) {
294     var position = canvas.getCursor().getPosition(),
295         element = position.element,
296         nodeElement = element ? element.parent() : canvas.getCurrentNodeElement();
297
298     if(!nodeElement || !(nodeElement.parent())) {
299         return;
300     }
301
302     var range = nodeElement.wlxmlNode.unwrapContent();
303
304     if(element) {
305         var elementIsFirstChild = nodeElement.childIndex(element);
306         if(element.bound()) {
307             canvas.setCurrentElement(element, {caretTo: position.offset});
308         } else {
309             if(elementIsFirstChild) {
310                 canvas.setCurrentElement(utils.findCanvasElement(range.element1), {caretTo: 'end'});
311             } else {
312                 canvas.setCurrentElement(utils.findCanvasElement(range.element2), {caretTo: 'end'});
313             }
314         }
315     } else {
316         canvas.setCurrentElement(utils.findCanvasElement(range.element1), {caretTo: 'start'});
317     }
318
319 });
320
321
322 return {
323     run: function(name, params, canvas, user) {
324         return commands.run(name, params, canvas, user);
325     }
326 };
327
328 });