fnpjs: context menu support
[fnpeditor.git] / src / editor / modules / documentCanvas / canvas / comments / comments.js
1 define(function(require) {
2     
3 'use strict';
4 /* globals gettext */
5
6
7 var $ = require('libs/jquery'),
8     _ = require('libs/underscore'),
9     datetime = require('fnpjs/datetime'),
10     commentsTemplate = require('libs/text!./comments.html'),
11     commentTemplate = require('libs/text!./comment.html');
12
13
14 var makeAutoResizable = function(textarea) {
15     textarea.on('input', function() {
16         resize(textarea);
17     });
18 };
19
20 var resize = function(textarea) {
21     if(textarea.prop('scrollHeight') > textarea.prop('clientHeight')) {
22         textarea.height(textarea.prop('scrollHeight'));
23     }
24 };
25
26
27 var CommentsView = function(node, user) {
28     this.node = node;
29     this.user = user;
30     this.dom = $(_.template(commentsTemplate)());
31     this.list = this.dom.find('.list');
32     this.textarea = this.dom.find('textarea');
33     this.addButton = this.dom.find('button.btnAdd');
34     this.cancelButton = this.dom.find('button.btnCancel');
35
36     this.textarea.on('input', function() {
37         this.addButton.attr('disabled', this.textarea.val() === '');
38     }.bind(this));
39     makeAutoResizable(this.textarea);
40
41     this.addButton.hide();
42     this.cancelButton.hide();
43     this.textarea.on('focus', function() {
44         this.addButton.show();
45         this.cancelButton.show();
46     }.bind(this));
47
48     this.addButton.on('click', function() {
49         if(!this.node) {
50             return;
51         }
52
53         this.node.document.transaction(function() {
54             var commentNode = this.node.document.createDocumentNode({tagName: 'aside', attrs: {'class': 'comment'}}),
55                 metadata = commentNode.getMetadata(),
56                 creator;
57
58             if(this.user) {
59                 creator = this.user.name;
60                 if(this.user.email) {
61                     creator += ' (' + this.user.email + ')';
62                 }
63             } else {
64                 creator = 'anonymous';
65             }
66
67             metadata.add({key: 'creator', value: creator});
68             metadata.add({key: 'date', value: datetime.currentStrfmt()});
69             commentNode.append({text: this.textarea.val()});
70
71             this.node.append(commentNode);
72         }.bind(this), {
73             metadata: {
74                 description: gettext('Add comment')
75             },
76             success: function() {
77                 this.textarea.val('');
78             }.bind(this)
79         });
80
81     }.bind(this));
82
83     this.cancelButton.on('click', function() {
84         this.addButton.hide();
85         this.cancelButton.hide();
86         this.textarea.val('');
87     }.bind(this));
88
89     this.render();
90     this.onDeactivated();
91
92 };
93
94 _.extend(CommentsView.prototype, {
95     render: function() {
96         this.list.empty();
97         this.textarea.attr('placeholder', gettext('Comment'));
98
99         this.node.contents()
100             .filter(function(child) {
101                 return child.is({tag: 'aside', klass: 'comment'});
102             })
103             .forEach(function(commentNode) {
104                 var commentView = new CommentView(commentNode);
105                 this.list.append(commentView.dom);
106                 this.textarea.attr('placeholder', gettext('Respond') + '...');
107             }.bind(this));
108     },
109     onActivated: function() {
110             this.dom.find('.newComment').toggle(true);
111     },
112     onDeactivated: function() {
113       this.dom.find('.newComment').toggle(false);
114       this.addButton.hide();
115       this.cancelButton.hide();
116     },
117 });
118
119
120 var CommentView = function(commentNode) {
121     this.node = commentNode;
122
123     var metaData = this.node.getMetadata(),
124         author, date;
125
126     metaData.some(function(row) {
127         author = row.getValue();
128         if(author) {
129             author = author.split(' ')[0];
130         }
131         return true;
132     }, 'creator');
133     
134     metaData.some(function(row) {
135         date = row.getValue();
136         if(/[0-9][0-9]:[0-9][0-9]:[0-9][0-9]$/g.test(date)) {
137             date = date.split(':');
138             date.pop();
139             date = date.join(':');
140         }
141         return true;
142     }, 'date');
143
144     this.dom = $(_.template(commentTemplate)({
145         author: author || '',
146         date: date || '',
147         content: this.node.object.getText() || '?'
148     }));
149
150     this.contentElement = this.dom.find('.content');
151     this.editElement = this.dom.find('.edit');
152     this.deleteDialogElement = this.dom.find('.deleteDialog');
153
154     this.dom.find('.remove-btn').on('click', function() {
155         this.deleteDialogElement.show();
156     }.bind(this));
157
158     this.dom.find('.deleteDialog-confirm').on('click', function() {
159         this.node.document.transaction(function() {
160             this.node.detach();
161         }.bind(this), {
162             metadata: {
163                 description: gettext('Remove comment')
164             }
165         });
166     }.bind(this));
167
168     this.dom.find('.deleteDialog-cancel').on('click', function() {
169         this.deleteDialogElement.hide();
170     }.bind(this));
171
172     this.dom.find('.edit-start-btn').on('click', function() {
173         this.startEditing();
174     }.bind(this));
175
176     this.dom.find('.edit-save-btn').on('click', function() {
177         this.saveEditing();
178     }.bind(this));
179
180     this.dom.find('.edit-cancel-btn').on('click', function() {
181         this.cancelEditing();
182     }.bind(this));
183
184     this.textarea = this.editElement.find('textarea');
185     this.textarea.on('input', function() {
186         this.dom.find('.edit-save-btn').attr('disabled', this.textarea.val() === '');
187     }.bind(this));
188     makeAutoResizable(this.textarea);
189 };
190
191 $.extend(CommentView.prototype, {
192     startEditing: function() {
193         this.contentElement.hide();
194         this.editElement.show();
195         this.textarea.val(this.node.object.getText());
196         resize(this.textarea);
197         this.textarea.focus();
198     },
199     saveEditing: function() {
200         var newContent = this.editElement.find('textarea').val();
201         this.node.document.transaction(function() {
202             this.node.object.setText(newContent);
203         }.bind(this), {
204             metadata: {
205                 description: gettext('Edit comment')
206             }
207         });
208     },
209     cancelEditing: function() {
210         this.contentElement.show();
211         this.editElement.find('textarea').val('');
212         this.editElement.hide();
213     },
214 });
215
216
217 return CommentsView;
218
219 });