MDL-42023 assign: Edit PDF plugin - Damyon's contributions
[moodle.git] / mod / assign / feedback / editpdf / yui / src / editor / js / comment.js
CommitLineData
5c386472
DW
1// This file is part of Moodle - http://moodle.org/
2//
3// Moodle is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// Moodle is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
15
16/**
17 * Class representing a list of comments.
18 *
19 * @module moodle-assignfeedback_editpdf-editor
20 */
21
22/**
23 * COMMENT
24 *
25 * @namespace M.assignfeedback_editpdf
26 * @class comment
27 * @param M.assignfeedback_editpdf.editor editor
28 * @param Int gradeid
29 * @param Int pageno
30 * @param Int x
31 * @param Int y
32 * @param Int width
33 * @param String colour
34 * @param String rawtext
35 */
36COMMENT = function(editor, gradeid, pageno, x, y, width, colour, rawtext) {
37
38 /**
39 * Reference to M.assignfeedback_editpdf.editor.
40 * @property editor
41 * @type M.assignfeedback_editpdf.editor
42 * @public
43 */
44 this.editor = editor;
45
46 /**
47 * Grade id
48 * @property gradeid
49 * @type Int
50 * @public
51 */
52 this.gradeid = gradeid || 0;
53
54 /**
55 * X position
56 * @property x
57 * @type Int
58 * @public
59 */
60 this.x = parseInt(x, 10) || 0;
61
62 /**
63 * Y position
64 * @property y
65 * @type Int
66 * @public
67 */
68 this.y = parseInt(y, 10) || 0;
69
70 /**
71 * Comment width
72 * @property width
73 * @type Int
74 * @public
75 */
76 this.width = parseInt(width, 10) || 0;
77
78 /**
79 * Comment rawtext
80 * @property rawtext
81 * @type String
82 * @public
83 */
84 this.rawtext = rawtext || '';
85
86 /**
87 * Comment page number
88 * @property pageno
89 * @type Int
90 * @public
91 */
92 this.pageno = pageno || 0;
93
94 /**
95 * Comment background colour.
96 * @property colour
97 * @type String
98 * @public
99 */
100 this.colour = colour || 'yellow';
101
102 /**
103 * Reference to M.assignfeedback_editpdf.drawable
104 * @property drawable
105 * @type M.assignfeedback_editpdf.drawable
106 * @public
107 */
108 this.drawable = false;
109
110 /**
111 * Boolean used by a timeout to delete empty comments after a short delay.
112 * @property deleteme
113 * @type Boolean
114 * @public
115 */
116 this.deleteme = false;
117
118 /**
119 * Reference to the link that opens the menu.
120 * @property menulink
121 * @type Y.Node
122 * @public
123 */
124 this.menulink = null;
125
126 /**
127 * Reference to the dialogue that is the context menu.
128 * @property menu
129 * @type M.assignfeedback_editpdf.dropdown
130 * @public
131 */
132 this.menu = null;
133
134 /**
135 * Clean a comment record, returning an oject with only fields that are valid.
136 * @public
137 * @method clean
138 * @return {}
139 */
140 this.clean = function() {
141 return {
142 gradeid : this.gradeid,
143 x : parseInt(this.x, 10),
144 y : parseInt(this.y, 10),
145 width : parseInt(this.width, 10),
146 rawtext : this.rawtext,
147 pageno : this.currentpage,
148 colour : this.colour
149 };
150 };
151
152 /**
153 * Draw a comment.
154 * @public
155 * @method draw_comment
156 * @param boolean focus - Set the keyboard focus to the new comment if true
157 * @return M.assignfeedback_editpdf.drawable
158 */
159 this.draw = function(focus) {
160 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
161 node,
162 drawingregion = Y.one(SELECTOR.DRAWINGREGION),
163 container,
164 menu,
165 position,
166 scrollheight;
167
168 // Lets add a contenteditable div.
169 node = Y.Node.create('<textarea/>');
170 container = Y.Node.create('<div class="commentdrawable"/>');
171 menu = Y.Node.create('<a href="#"><img src="' + M.util.image_url('t/contextmenu', 'core') + '"/></a>');
172
173 this.menulink = menu;
174 container.append(node);
175
176 if (!this.editor.get('readonly')) {
177 container.append(menu);
178 } else {
179 node.setAttribute('readonly', 'readonly');
180 }
181 if (this.width < 100) {
182 this.width = 100;
183 }
184
185 position = this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(this.x, this.y));
186 node.setStyles({
187 width: this.width + 'px',
188 backgroundColor: COMMENTCOLOUR[this.colour]
189 });
190
191 drawingregion.append(container);
192 container.setX(position.x);
193 container.setY(position.y);
194 drawable.nodes.push(container);
195 node.set('value', this.rawtext);
196 scrollheight = node.get('scrollHeight'),
197 node.setStyles({
198 'height' : scrollheight + 'px',
199 'overflow': 'hidden'
200 });
201 if (!this.editor.get('readonly')) {
202 this.attach_events(node, menu);
203 }
204 if (focus) {
205 node.focus();
206 }
207 this.drawable = drawable;
208
209
210 return drawable;
211 };
212
213 /**
214 * Delete an empty comment if it's menu hasn't been opened in time.
215 * @method delete_comment_later
216 */
217 this.delete_comment_later = function() {
218 if (this.deleteme) {
219 this.remove();
220 }
221 };
222
223 /**
224 * Comment nodes have a bunch of event handlers attached to them directly.
225 * This is all done here for neatness.
226 *
227 * @protected
228 * @method attach_comment_events
229 * @param node - The Y.Node representing the comment.
230 * @param menu - The Y.Node representing the menu.
231 */
232 this.attach_events = function(node, menu) {
233 // Save the text on blur.
234 node.on('blur', function() {
235 // Save the changes back to the comment.
236 this.rawtext = node.get('value');
237 this.width = parseInt(node.getStyle('width'), 10);
238
239 // Trim.
240 if (this.rawtext.replace(/^\s+|\s+$/g, "") === '') {
241 // Delete empty comments.
242 this.deleteme = true;
243 Y.later(400, this, this.delete_comment_later);
244 }
245 this.editor.save_current_page();
246 }, this);
247
248 // For delegated event handler.
249 menu.setData('comment', this);
250
251 node.on('keyup', function() {
252 var scrollheight = node.get('scrollHeight'),
253 height = parseInt(node.getStyle('height'), 10);
254
255 // Webkit scrollheight fix.
256 if (scrollheight === height + 8) {
257 scrollheight -= 8;
258 }
259 node.setStyle('height', scrollheight + 'px');
260
261 });
262
263 node.on('gesturemovestart', function(e) {
264 node.setData('dragging', true);
265 node.setData('offsetx', e.clientX - node.getX());
266 node.setData('offsety', e.clientY - node.getY());
267 });
268 node.on('gesturemoveend', function() {
269 node.setData('dragging', false);
270 this.editor.save_current_page();
271 }, null, this);
272 node.on('gesturemove', function(e) {
273 var x = e.clientX - node.getData('offsetx'),
274 y = e.clientY - node.getData('offsety'),
275 nodewidth,
276 nodeheight,
277 newlocation,
278 windowlocation,
279 bounds;
280
281 nodewidth = parseInt(node.getStyle('width'), 10);
282 nodeheight = parseInt(node.getStyle('height'), 10);
283
284 newlocation = this.editor.get_canvas_coordinates(new M.assignfeedback_editpdf.point(x, y));
285 bounds = this.editor.get_canvas_bounds(true);
286 bounds.x = 0;
287 bounds.y = 0;
288
289 bounds.width -= nodewidth + 42;
290 bounds.height -= nodeheight + 8;
291 // Clip to the window size - the comment size.
292 newlocation.clip(bounds);
293
294 this.x = newlocation.x;
295 this.y = newlocation.y;
296
297 windowlocation = this.editor.get_window_coordinates(newlocation);
298 node.ancestor().setX(windowlocation.x);
299 node.ancestor().setY(windowlocation.y);
300 }, null, this);
301
302 this.menu = new M.assignfeedback_editpdf.commentmenu({
303 buttonNode: this.menulink,
304 comment: this
305 });
306 };
307
308 /**
309 * Delete a comment.
310 * @method remove
311 */
312 this.remove = function() {
313 var i = 0, comments;
314
315 comments = this.editor.pages[this.editor.currentpage].comments;
316 for (i = 0; i < comments.length; i++) {
317 if (comments[i] === this) {
318 comments.splice(i, 1);
319 this.drawable.erase();
320 this.editor.save_current_page();
321 return;
322 }
323 }
324 };
325
326 /**
327 * Event handler to remove a comment from the users quicklist.
328 *
329 * @protected
330 * @method remove_from_quicklist
331 */
332 this.remove_from_quicklist = function(e, quickcomment) {
333 this.menu.hide();
334
335 this.editor.quicklist.remove(quickcomment);
336 };
337
338 /**
339 * A quick comment was selected in the list, update the active comment and redraw the page.
340 *
341 * @param Event e
342 * @protected
343 * @method set_from_quick_comment
344 */
345 this.set_from_quick_comment = function(e, quickcomment) {
346 this.menu.hide();
347
348 this.rawtext = quickcomment.rawtext;
349 this.width = quickcomment.width;
350 this.colour = quickcomment.colour;
351
352 this.editor.save_current_page();
353
354 this.editor.redraw();
355 };
356
357 /**
358 * Event handler to add a comment to the users quicklist.
359 *
360 * @protected
361 * @method add_to_quicklist
362 */
363 this.add_to_quicklist = function() {
364 this.menu.hide();
365 this.editor.quicklist.add(this);
366 };
367
368 /**
369 * Draw the in progress edit.
370 *
371 * @public
372 * @method draw_current_edit
373 * @param M.assignfeedback_editpdf.edit edit
374 */
375 this.draw_current_edit = function(edit) {
376 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
377 shape,
378 bounds;
379
380 bounds = new M.assignfeedback_editpdf.rect();
381 bounds.bound([edit.start, edit.end]);
382
383 // We will draw a box with the current background colour.
384 shape = this.editor.graphic.addShape({
385 type: Y.Rect,
386 width: bounds.width,
387 height: bounds.height,
388 fill: {
389 color: COMMENTCOLOUR[edit.commentcolour]
390 },
391 x: bounds.x,
392 y: bounds.y
393 });
394
395 drawable.shapes.push(shape);
396
397 return drawable;
398 };
399
400 /**
401 * Promote the current edit to a real comment.
402 *
403 * @public
404 * @method init_from_edit
405 * @param M.assignfeedback_editpdf.edit edit
406 */
407 this.init_from_edit = function(edit) {
408 var bounds = new M.assignfeedback_editpdf.rect();
409 bounds.bound([edit.start, edit.end]);
410
411 // Minimum comment width.
412 if (bounds.width < 100) {
413 bounds.width = 100;
414 }
415
416 // Save the current edit to the server and the current page list.
417
418 this.gradeid = this.editor.get('gradeid');
419 this.pageno = this.editor.currentpage;
420 this.x = bounds.x;
421 this.y = bounds.y;
422 this.width = bounds.width;
423 this.colour = edit.commentcolour;
424 this.rawtext = '';
425 };
426
427};
428
429M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
430M.assignfeedback_editpdf.comment = COMMENT;