1 YUI.add('moodle-assignfeedback_editpdf-editor', function (Y, NAME) {
3 // This file is part of Moodle - http://moodle.org/
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 /* eslint-disable no-unused-vars */
20 * A list of globals used by this module.
22 * @module moodle-assignfeedback_editpdf-editor
24 var AJAXBASE = M.cfg.wwwroot + '/mod/assign/feedback/editpdf/ajax.php',
25 AJAXBASEPROGRESS = M.cfg.wwwroot + '/mod/assign/feedback/editpdf/ajax_progress.php',
27 DIALOGUE: 'assignfeedback_editpdf_widget'
30 PREVIOUSBUTTON: '.navigate-previous-button',
31 NEXTBUTTON: ' .navigate-next-button',
32 SEARCHCOMMENTSBUTTON: '.searchcommentsbutton',
33 SEARCHFILTER: '.assignfeedback_editpdf_commentsearch input',
34 SEARCHCOMMENTSLIST: '.assignfeedback_editpdf_commentsearch ul',
35 PAGESELECT: '.navigate-page-select',
36 LOADINGICON: '.loading',
37 PROGRESSBARCONTAINER: '.progress-info.progress-striped',
38 DRAWINGREGION: '.drawingregion',
39 DRAWINGCANVAS: '.drawingcanvas',
41 COMMENTCOLOURBUTTON: '.commentcolourbutton',
42 COMMENTMENU: '.commentdrawable a',
43 ANNOTATIONCOLOURBUTTON: '.annotationcolourbutton',
44 DELETEANNOTATIONBUTTON: '.deleteannotationbutton',
45 UNSAVEDCHANGESDIV: '.assignfeedback_editpdf_unsavedchanges',
46 UNSAVEDCHANGESINPUT: 'input[name="assignfeedback_editpdf_haschanges"]',
47 STAMPSBUTTON: '.currentstampbutton',
48 DIALOGUE: '.' + CSS.DIALOGUE
50 SELECTEDBORDERCOLOUR = 'rgba(200, 200, 255, 0.9)',
51 SELECTEDFILLCOLOUR = 'rgba(200, 200, 255, 0.5)',
52 COMMENTTEXTCOLOUR = 'rgb(51, 51, 51)',
54 'white': 'rgb(255,255,255)',
55 'yellow': 'rgb(255,236,174)',
56 'red': 'rgb(249,181,179)',
57 'green': 'rgb(214,234,178)',
58 'blue': 'rgb(203,217,237)',
59 'clear': 'rgba(255,255,255, 0)'
62 'white': 'rgb(255,255,255)',
63 'yellow': 'rgb(255,207,53)',
64 'red': 'rgb(239,69,64)',
65 'green': 'rgb(152,202,62)',
66 'blue': 'rgb(125,159,211)',
67 'black': 'rgb(51,51,51)'
71 'comment': '.commentbutton',
73 'line': '.linebutton',
74 'rectangle': '.rectanglebutton',
75 'oval': '.ovalbutton',
76 'stamp': '.stampbutton',
77 'select': '.selectbutton',
78 'drag': '.dragbutton',
79 'highlight': '.highlightbutton'
82 // This file is part of Moodle - http://moodle.org/
84 // Moodle is free software: you can redistribute it and/or modify
85 // it under the terms of the GNU General Public License as published by
86 // the Free Software Foundation, either version 3 of the License, or
87 // (at your option) any later version.
89 // Moodle is distributed in the hope that it will be useful,
90 // but WITHOUT ANY WARRANTY; without even the implied warranty of
91 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
92 // GNU General Public License for more details.
94 // You should have received a copy of the GNU General Public License
95 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
98 * Provides an in browser PDF editor.
100 * @module moodle-assignfeedback_editpdf-editor
104 * Class representing a 2d point.
106 * @namespace M.assignfeedback_editpdf
111 var POINT = function(x, y) {
119 this.x = parseInt(x, 10);
127 this.y = parseInt(y, 10);
130 * Clip this point to the rect
132 * @param M.assignfeedback_editpdf.point
135 this.clip = function(bounds) {
136 if (this.x < bounds.x) {
139 if (this.x > (bounds.x + bounds.width)) {
140 this.x = bounds.x + bounds.width;
142 if (this.y < bounds.y) {
145 if (this.y > (bounds.y + bounds.height)) {
146 this.y = bounds.y + bounds.height;
153 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
154 M.assignfeedback_editpdf.point = POINT;
155 // This file is part of Moodle - http://moodle.org/
157 // Moodle is free software: you can redistribute it and/or modify
158 // it under the terms of the GNU General Public License as published by
159 // the Free Software Foundation, either version 3 of the License, or
160 // (at your option) any later version.
162 // Moodle is distributed in the hope that it will be useful,
163 // but WITHOUT ANY WARRANTY; without even the implied warranty of
164 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
165 // GNU General Public License for more details.
167 // You should have received a copy of the GNU General Public License
168 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
171 * Provides an in browser PDF editor.
173 * @module moodle-assignfeedback_editpdf-editor
177 * Class representing a 2d rect.
179 * @namespace M.assignfeedback_editpdf
186 var RECT = function(x, y, width, height) {
218 this.height = height;
221 * Set this rect to represent the smallest possible rectangle containing this list of points.
223 * @param M.assignfeedback_editpdf.point[]
226 this.bound = function(points) {
234 for (i = 0; i < points.length; i++) {
236 if (point.x < minx || i === 0) {
239 if (point.x > maxx || i === 0) {
242 if (point.y < miny || i === 0) {
245 if (point.y > maxy || i === 0) {
251 this.width = maxx - minx;
252 this.height = maxy - miny;
258 * Checks if rect has min width.
259 * @method has_min_width
260 * @return bool true if width is more than 5px.
263 this.has_min_width = function() {
264 return (this.width >= 5);
268 * Checks if rect has min height.
269 * @method has_min_height
270 * @return bool true if height is more than 5px.
273 this.has_min_height = function() {
274 return (this.height >= 5);
278 * Set min. width of annotation bound.
279 * @method set_min_width
282 this.set_min_width = function() {
287 * Set min. height of annotation bound.
288 * @method set_min_height
291 this.set_min_height = function() {
296 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
297 M.assignfeedback_editpdf.rect = RECT;
298 // This file is part of Moodle - http://moodle.org/
300 // Moodle is free software: you can redistribute it and/or modify
301 // it under the terms of the GNU General Public License as published by
302 // the Free Software Foundation, either version 3 of the License, or
303 // (at your option) any later version.
305 // Moodle is distributed in the hope that it will be useful,
306 // but WITHOUT ANY WARRANTY; without even the implied warranty of
307 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
308 // GNU General Public License for more details.
310 // You should have received a copy of the GNU General Public License
311 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
314 * Provides an in browser PDF editor.
316 * @module moodle-assignfeedback_editpdf-editor
322 * @namespace M.assignfeedback_editpdf
325 var EDIT = function() {
328 * Starting point for the edit.
330 * @type M.assignfeedback_editpdf.point|false
336 * Finishing point for the edit.
338 * @type M.assignfeedback_editpdf.point|false
344 * Starting time for the edit.
345 * @property starttime
352 * Starting point for the currently selected annotation.
353 * @property annotationstart
354 * @type M.assignfeedback_editpdf.point|false
357 this.annotationstart = false;
360 * The currently selected tool
368 * The currently comment colour
369 * @property commentcolour
373 this.commentcolour = 'yellow';
376 * The currently annotation colour
377 * @property annotationcolour
381 this.annotationcolour = 'red';
384 * The current stamp image.
392 * List of points the the current drawing path.
394 * @type M.assignfeedback_editpdf.point[]
400 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
401 M.assignfeedback_editpdf.edit = EDIT;
402 // This file is part of Moodle - http://moodle.org/
404 // Moodle is free software: you can redistribute it and/or modify
405 // it under the terms of the GNU General Public License as published by
406 // the Free Software Foundation, either version 3 of the License, or
407 // (at your option) any later version.
409 // Moodle is distributed in the hope that it will be useful,
410 // but WITHOUT ANY WARRANTY; without even the implied warranty of
411 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
412 // GNU General Public License for more details.
414 // You should have received a copy of the GNU General Public License
415 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
416 /* global SELECTOR */
419 * Provides an in browser PDF editor.
421 * @module moodle-assignfeedback_editpdf-editor
425 * Class representing a drawable thing which contains both Y.Nodes, and Y.Shapes.
427 * @namespace M.assignfeedback_editpdf
428 * @param M.assignfeedback_editpdf.editor editor
431 var DRAWABLE = function(editor) {
434 * Reference to M.assignfeedback_editpdf.editor.
436 * @type M.assignfeedback_editpdf.editor
439 this.editor = editor;
458 * Delete the shapes from the drawable.
460 * @method erase_drawable
462 this.erase = function() {
464 while (this.shapes.length > 0) {
465 this.editor.graphic.removeShape(this.shapes.pop());
469 while (this.nodes.length > 0) {
470 this.nodes.pop().remove();
476 * Update the positions of all absolutely positioned nodes, when the drawing canvas is scrolled
478 * @method scroll_update
482 this.scroll_update = function(scrollx, scrolly) {
484 for (i = 0; i < this.nodes.length; i++) {
485 x = this.nodes[i].getData('x');
486 y = this.nodes[i].getData('y');
487 if (x !== undefined && y !== undefined) {
488 this.nodes[i].setX(parseInt(x, 10) - scrollx);
489 this.nodes[i].setY(parseInt(y, 10) - scrolly);
495 * Store the initial position of the node, so it can be updated when the drawing canvas is scrolled
497 * @method store_position
502 this.store_position = function(container, x, y) {
503 var drawingregion, scrollx, scrolly;
505 drawingregion = this.editor.get_dialogue_element(SELECTOR.DRAWINGREGION);
506 scrollx = parseInt(drawingregion.get('scrollLeft'), 10);
507 scrolly = parseInt(drawingregion.get('scrollTop'), 10);
508 container.setData('x', x + scrollx);
509 container.setData('y', y + scrolly);
513 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
514 M.assignfeedback_editpdf.drawable = DRAWABLE;
515 // This file is part of Moodle - http://moodle.org/
517 // Moodle is free software: you can redistribute it and/or modify
518 // it under the terms of the GNU General Public License as published by
519 // the Free Software Foundation, either version 3 of the License, or
520 // (at your option) any later version.
522 // Moodle is distributed in the hope that it will be useful,
523 // but WITHOUT ANY WARRANTY; without even the implied warranty of
524 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
525 // GNU General Public License for more details.
527 // You should have received a copy of the GNU General Public License
528 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
529 /* global STROKEWEIGHT, SELECTOR, SELECTEDBORDERCOLOUR, SELECTEDFILLCOLOUR */
532 * Provides an in browser PDF editor.
534 * @module moodle-assignfeedback_editpdf-editor
538 * Class representing a highlight.
540 * @namespace M.assignfeedback_editpdf
544 var ANNOTATION = function(config) {
545 ANNOTATION.superclass.constructor.apply(this, [config]);
548 ANNOTATION.NAME = "annotation";
549 ANNOTATION.ATTRS = {};
551 Y.extend(ANNOTATION, Y.Base, {
553 * Reference to M.assignfeedback_editpdf.editor.
555 * @type M.assignfeedback_editpdf.editor
569 * Comment page number
611 * @type String - list of points like x1,y1:x2,y2
633 * Reference to M.assignfeedback_editpdf.drawable
635 * @type M.assignfeedback_editpdf.drawable
641 * Initialise the annotation.
643 * @method initializer
646 initializer: function(config) {
647 this.editor = config.editor || null;
648 this.gradeid = parseInt(config.gradeid, 10) || 0;
649 this.pageno = parseInt(config.pageno, 10) || 0;
650 this.x = parseInt(config.x, 10) || 0;
651 this.y = parseInt(config.y, 10) || 0;
652 this.endx = parseInt(config.endx, 10) || 0;
653 this.endy = parseInt(config.endy, 10) || 0;
654 this.path = config.path || '';
655 this.type = config.type || 'rect';
656 this.colour = config.colour || 'red';
657 this.drawable = false;
661 * Clean a comment record, returning an oject with only fields that are valid.
668 gradeid: this.gradeid,
669 x: parseInt(this.x, 10),
670 y: parseInt(this.y, 10),
671 endx: parseInt(this.endx, 10),
672 endy: parseInt(this.endy, 10),
681 * Draw a selection around this annotation if it is selected.
683 * @method draw_highlight
684 * @return M.assignfeedback_editpdf.drawable
686 draw_highlight: function() {
688 drawingregion = this.editor.get_dialogue_element(SELECTOR.DRAWINGREGION),
689 offsetcanvas = this.editor.get_dialogue_element(SELECTOR.DRAWINGCANVAS).getXY(),
692 if (this.editor.currentannotation === this) {
693 // Draw a highlight around the annotation.
694 bounds = new M.assignfeedback_editpdf.rect();
695 bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y),
696 new M.assignfeedback_editpdf.point(this.endx, this.endy)]);
698 shape = this.editor.graphic.addShape({
701 height: bounds.height,
703 weight: STROKEWEIGHT,
704 color: SELECTEDBORDERCOLOUR
707 color: SELECTEDFILLCOLOUR
712 this.drawable.shapes.push(shape);
714 // Add a delete X to the annotation.
715 var deleteicon = Y.Node.create('<img src="' + M.util.image_url('trash', 'assignfeedback_editpdf') + '"/>'),
716 deletelink = Y.Node.create('<a href="#" role="button"></a>');
718 deleteicon.setAttrs({
719 'alt': M.util.get_string('deleteannotation', 'assignfeedback_editpdf')
721 deleteicon.setStyles({
722 'backgroundColor': 'white'
724 deletelink.addClass('deleteannotationbutton');
725 deletelink.append(deleteicon);
727 drawingregion.append(deletelink);
728 deletelink.setData('annotation', this);
729 deletelink.setStyle('zIndex', '200');
731 deletelink.on('click', this.remove, this);
732 deletelink.on('key', this.remove, 'space,enter', this);
734 deletelink.setX(offsetcanvas[0] + bounds.x + bounds.width - 18);
735 deletelink.setY(offsetcanvas[1] + bounds.y + 6);
736 this.drawable.nodes.push(deletelink);
738 return this.drawable;
745 * @return M.assignfeedback_editpdf.drawable|false
748 // Should be overridden by the subclass.
749 this.draw_highlight();
750 return this.drawable;
754 * Delete an annotation
759 remove: function(e) {
765 annotations = this.editor.pages[this.editor.currentpage].annotations;
766 for (i = 0; i < annotations.length; i++) {
767 if (annotations[i] === this) {
768 annotations.splice(i, 1);
770 this.drawable.erase();
772 this.editor.currentannotation = false;
773 this.editor.save_current_page();
780 * Move an annotation to a new location.
784 * @method move_annotation
786 move: function(newx, newy) {
787 var diffx = newx - this.x,
788 diffy = newy - this.y,
789 newpath, oldpath, xy,
799 oldpath = this.path.split(':');
800 Y.each(oldpath, function(position) {
801 xy = position.split(',');
802 x = parseInt(xy[0], 10);
803 y = parseInt(xy[1], 10);
804 newpath.push((x + diffx) + ',' + (y + diffy));
807 this.path = newpath.join(':');
811 this.drawable.erase();
813 this.editor.drawables.push(this.draw());
817 * Draw the in progress edit.
820 * @method draw_current_edit
821 * @param M.assignfeedback_editpdf.edit edit
823 draw_current_edit: function(edit) {
824 var noop = edit && false;
825 // Override me please.
830 * Promote the current edit to a real annotation.
833 * @method init_from_edit
834 * @param M.assignfeedback_editpdf.edit edit
835 * @return bool if width/height is more than min. required.
837 init_from_edit: function(edit) {
838 var bounds = new M.assignfeedback_editpdf.rect();
839 bounds.bound([edit.start, edit.end]);
841 this.gradeid = this.editor.get('gradeid');
842 this.pageno = this.editor.currentpage;
845 this.endx = bounds.x + bounds.width;
846 this.endy = bounds.y + bounds.height;
847 this.colour = edit.annotationcolour;
849 return (bounds.has_min_width() && bounds.has_min_height());
854 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
855 M.assignfeedback_editpdf.annotation = ANNOTATION;
856 // This file is part of Moodle - http://moodle.org/
858 // Moodle is free software: you can redistribute it and/or modify
859 // it under the terms of the GNU General Public License as published by
860 // the Free Software Foundation, either version 3 of the License, or
861 // (at your option) any later version.
863 // Moodle is distributed in the hope that it will be useful,
864 // but WITHOUT ANY WARRANTY; without even the implied warranty of
865 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
866 // GNU General Public License for more details.
868 // You should have received a copy of the GNU General Public License
869 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
870 /* global STROKEWEIGHT, ANNOTATIONCOLOUR */
873 * Provides an in browser PDF editor.
875 * @module moodle-assignfeedback_editpdf-editor
879 * Class representing a line.
881 * @namespace M.assignfeedback_editpdf
882 * @class annotationline
883 * @extends M.assignfeedback_editpdf.annotation
885 var ANNOTATIONLINE = function(config) {
886 ANNOTATIONLINE.superclass.constructor.apply(this, [config]);
889 ANNOTATIONLINE.NAME = "annotationline";
890 ANNOTATIONLINE.ATTRS = {};
892 Y.extend(ANNOTATIONLINE, M.assignfeedback_editpdf.annotation, {
894 * Draw a line annotation
897 * @return M.assignfeedback_editpdf.drawable
903 drawable = new M.assignfeedback_editpdf.drawable(this.editor);
905 shape = this.editor.graphic.addShape({
909 weight: STROKEWEIGHT,
910 color: ANNOTATIONCOLOUR[this.colour]
914 shape.moveTo(this.x, this.y);
915 shape.lineTo(this.endx, this.endy);
917 drawable.shapes.push(shape);
918 this.drawable = drawable;
920 return ANNOTATIONLINE.superclass.draw.apply(this);
924 * Draw the in progress edit.
927 * @method draw_current_edit
928 * @param M.assignfeedback_editpdf.edit edit
930 draw_current_edit: function(edit) {
931 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
934 shape = this.editor.graphic.addShape({
938 weight: STROKEWEIGHT,
939 color: ANNOTATIONCOLOUR[edit.annotationcolour]
943 shape.moveTo(edit.start.x, edit.start.y);
944 shape.lineTo(edit.end.x, edit.end.y);
947 drawable.shapes.push(shape);
953 * Promote the current edit to a real annotation.
956 * @method init_from_edit
957 * @param M.assignfeedback_editpdf.edit edit
958 * @return bool true if line bound is more than min width/height, else false.
960 init_from_edit: function(edit) {
961 this.gradeid = this.editor.get('gradeid');
962 this.pageno = this.editor.currentpage;
963 this.x = edit.start.x;
964 this.y = edit.start.y;
965 this.endx = edit.end.x;
966 this.endy = edit.end.y;
967 this.colour = edit.annotationcolour;
970 return !(((this.endx - this.x) === 0) && ((this.endy - this.y) === 0));
975 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
976 M.assignfeedback_editpdf.annotationline = ANNOTATIONLINE;
977 // This file is part of Moodle - http://moodle.org/
979 // Moodle is free software: you can redistribute it and/or modify
980 // it under the terms of the GNU General Public License as published by
981 // the Free Software Foundation, either version 3 of the License, or
982 // (at your option) any later version.
984 // Moodle is distributed in the hope that it will be useful,
985 // but WITHOUT ANY WARRANTY; without even the implied warranty of
986 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
987 // GNU General Public License for more details.
989 // You should have received a copy of the GNU General Public License
990 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
991 /* global STROKEWEIGHT, ANNOTATIONCOLOUR */
994 * Provides an in browser PDF editor.
996 * @module moodle-assignfeedback_editpdf-editor
1000 * Class representing a rectangle.
1002 * @namespace M.assignfeedback_editpdf
1003 * @class annotationrectangle
1004 * @extends M.assignfeedback_editpdf.annotation
1006 var ANNOTATIONRECTANGLE = function(config) {
1007 ANNOTATIONRECTANGLE.superclass.constructor.apply(this, [config]);
1010 ANNOTATIONRECTANGLE.NAME = "annotationrectangle";
1011 ANNOTATIONRECTANGLE.ATTRS = {};
1013 Y.extend(ANNOTATIONRECTANGLE, M.assignfeedback_editpdf.annotation, {
1015 * Draw a rectangle annotation
1018 * @return M.assignfeedback_editpdf.drawable
1025 drawable = new M.assignfeedback_editpdf.drawable(this.editor);
1027 bounds = new M.assignfeedback_editpdf.rect();
1028 bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y),
1029 new M.assignfeedback_editpdf.point(this.endx, this.endy)]);
1031 shape = this.editor.graphic.addShape({
1033 width: bounds.width,
1034 height: bounds.height,
1036 weight: STROKEWEIGHT,
1037 color: ANNOTATIONCOLOUR[this.colour]
1042 drawable.shapes.push(shape);
1043 this.drawable = drawable;
1045 return ANNOTATIONRECTANGLE.superclass.draw.apply(this);
1049 * Draw the in progress edit.
1052 * @method draw_current_edit
1053 * @param M.assignfeedback_editpdf.edit edit
1055 draw_current_edit: function(edit) {
1056 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
1060 bounds = new M.assignfeedback_editpdf.rect();
1061 bounds.bound([new M.assignfeedback_editpdf.point(edit.start.x, edit.start.y),
1062 new M.assignfeedback_editpdf.point(edit.end.x, edit.end.y)]);
1064 // Set min. width and height of rectangle.
1065 if (!bounds.has_min_width()) {
1066 bounds.set_min_width();
1068 if (!bounds.has_min_height()) {
1069 bounds.set_min_height();
1072 shape = this.editor.graphic.addShape({
1074 width: bounds.width,
1075 height: bounds.height,
1077 weight: STROKEWEIGHT,
1078 color: ANNOTATIONCOLOUR[edit.annotationcolour]
1084 drawable.shapes.push(shape);
1090 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
1091 M.assignfeedback_editpdf.annotationrectangle = ANNOTATIONRECTANGLE;
1092 // This file is part of Moodle - http://moodle.org/
1094 // Moodle is free software: you can redistribute it and/or modify
1095 // it under the terms of the GNU General Public License as published by
1096 // the Free Software Foundation, either version 3 of the License, or
1097 // (at your option) any later version.
1099 // Moodle is distributed in the hope that it will be useful,
1100 // but WITHOUT ANY WARRANTY; without even the implied warranty of
1101 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1102 // GNU General Public License for more details.
1104 // You should have received a copy of the GNU General Public License
1105 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
1106 /* global STROKEWEIGHT, ANNOTATIONCOLOUR */
1109 * Provides an in browser PDF editor.
1111 * @module moodle-assignfeedback_editpdf-editor
1115 * Class representing a oval.
1117 * @namespace M.assignfeedback_editpdf
1118 * @class annotationoval
1119 * @extends M.assignfeedback_editpdf.annotation
1121 var ANNOTATIONOVAL = function(config) {
1122 ANNOTATIONOVAL.superclass.constructor.apply(this, [config]);
1125 ANNOTATIONOVAL.NAME = "annotationoval";
1126 ANNOTATIONOVAL.ATTRS = {};
1128 Y.extend(ANNOTATIONOVAL, M.assignfeedback_editpdf.annotation, {
1130 * Draw a oval annotation
1133 * @return M.assignfeedback_editpdf.drawable
1140 drawable = new M.assignfeedback_editpdf.drawable(this.editor);
1142 bounds = new M.assignfeedback_editpdf.rect();
1143 bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y),
1144 new M.assignfeedback_editpdf.point(this.endx, this.endy)]);
1146 shape = this.editor.graphic.addShape({
1148 width: bounds.width,
1149 height: bounds.height,
1151 weight: STROKEWEIGHT,
1152 color: ANNOTATIONCOLOUR[this.colour]
1157 drawable.shapes.push(shape);
1158 this.drawable = drawable;
1160 return ANNOTATIONOVAL.superclass.draw.apply(this);
1164 * Draw the in progress edit.
1167 * @method draw_current_edit
1168 * @param M.assignfeedback_editpdf.edit edit
1170 draw_current_edit: function(edit) {
1171 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
1175 bounds = new M.assignfeedback_editpdf.rect();
1176 bounds.bound([new M.assignfeedback_editpdf.point(edit.start.x, edit.start.y),
1177 new M.assignfeedback_editpdf.point(edit.end.x, edit.end.y)]);
1179 // Set min. width and height of oval.
1180 if (!bounds.has_min_width()) {
1181 bounds.set_min_width();
1183 if (!bounds.has_min_height()) {
1184 bounds.set_min_height();
1187 shape = this.editor.graphic.addShape({
1189 width: bounds.width,
1190 height: bounds.height,
1192 weight: STROKEWEIGHT,
1193 color: ANNOTATIONCOLOUR[edit.annotationcolour]
1199 drawable.shapes.push(shape);
1205 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
1206 M.assignfeedback_editpdf.annotationoval = ANNOTATIONOVAL;
1207 // This file is part of Moodle - http://moodle.org/
1209 // Moodle is free software: you can redistribute it and/or modify
1210 // it under the terms of the GNU General Public License as published by
1211 // the Free Software Foundation, either version 3 of the License, or
1212 // (at your option) any later version.
1214 // Moodle is distributed in the hope that it will be useful,
1215 // but WITHOUT ANY WARRANTY; without even the implied warranty of
1216 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1217 // GNU General Public License for more details.
1219 // You should have received a copy of the GNU General Public License
1220 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
1221 /* global STROKEWEIGHT, ANNOTATIONCOLOUR */
1224 * Provides an in browser PDF editor.
1226 * @module moodle-assignfeedback_editpdf-editor
1230 * Class representing a pen.
1232 * @namespace M.assignfeedback_editpdf
1233 * @class annotationpen
1234 * @extends M.assignfeedback_editpdf.annotation
1236 var ANNOTATIONPEN = function(config) {
1237 ANNOTATIONPEN.superclass.constructor.apply(this, [config]);
1240 ANNOTATIONPEN.NAME = "annotationpen";
1241 ANNOTATIONPEN.ATTRS = {};
1243 Y.extend(ANNOTATIONPEN, M.assignfeedback_editpdf.annotation, {
1245 * Draw a pen annotation
1248 * @return M.assignfeedback_editpdf.drawable
1257 drawable = new M.assignfeedback_editpdf.drawable(this.editor);
1259 shape = this.editor.graphic.addShape({
1263 weight: STROKEWEIGHT,
1264 color: ANNOTATIONCOLOUR[this.colour]
1269 // Recreate the pen path array.
1270 positions = this.path.split(':');
1271 // Redraw all the lines.
1272 Y.each(positions, function(position) {
1273 xy = position.split(',');
1275 shape.moveTo(xy[0], xy[1]);
1278 shape.lineTo(xy[0], xy[1]);
1284 drawable.shapes.push(shape);
1285 this.drawable = drawable;
1287 return ANNOTATIONPEN.superclass.draw.apply(this);
1291 * Draw the in progress edit.
1294 * @method draw_current_edit
1295 * @param M.assignfeedback_editpdf.edit edit
1297 draw_current_edit: function(edit) {
1298 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
1302 shape = this.editor.graphic.addShape({
1306 weight: STROKEWEIGHT,
1307 color: ANNOTATIONCOLOUR[edit.annotationcolour]
1312 // Recreate the pen path array.
1313 // Redraw all the lines.
1314 Y.each(edit.path, function(position) {
1316 shape.moveTo(position.x, position.y);
1319 shape.lineTo(position.x, position.y);
1325 drawable.shapes.push(shape);
1332 * Promote the current edit to a real annotation.
1335 * @method init_from_edit
1336 * @param M.assignfeedback_editpdf.edit edit
1337 * @return bool true if pen bound is more than min width/height, else false.
1339 init_from_edit: function(edit) {
1340 var bounds = new M.assignfeedback_editpdf.rect(),
1344 // This will get the boundaries of all points in the path.
1345 bounds.bound(edit.path);
1347 for (i = 0; i < edit.path.length; i++) {
1348 pathlist.push(parseInt(edit.path[i].x, 10) + ',' + parseInt(edit.path[i].y, 10));
1351 this.gradeid = this.editor.get('gradeid');
1352 this.pageno = this.editor.currentpage;
1355 this.endx = bounds.x + bounds.width;
1356 this.endy = bounds.y + bounds.height;
1357 this.colour = edit.annotationcolour;
1358 this.path = pathlist.join(':');
1360 return (bounds.has_min_width() || bounds.has_min_height());
1366 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
1367 M.assignfeedback_editpdf.annotationpen = ANNOTATIONPEN;
1368 // This file is part of Moodle - http://moodle.org/
1370 // Moodle is free software: you can redistribute it and/or modify
1371 // it under the terms of the GNU General Public License as published by
1372 // the Free Software Foundation, either version 3 of the License, or
1373 // (at your option) any later version.
1375 // Moodle is distributed in the hope that it will be useful,
1376 // but WITHOUT ANY WARRANTY; without even the implied warranty of
1377 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1378 // GNU General Public License for more details.
1380 // You should have received a copy of the GNU General Public License
1381 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
1382 /* global ANNOTATIONCOLOUR */
1385 * Provides an in browser PDF editor.
1387 * @module moodle-assignfeedback_editpdf-editor
1391 * Class representing a highlight.
1393 * @namespace M.assignfeedback_editpdf
1394 * @class annotationhighlight
1395 * @extends M.assignfeedback_editpdf.annotation
1396 * @module moodle-assignfeedback_editpdf-editor
1398 var ANNOTATIONHIGHLIGHT = function(config) {
1399 ANNOTATIONHIGHLIGHT.superclass.constructor.apply(this, [config]);
1402 ANNOTATIONHIGHLIGHT.NAME = "annotationhighlight";
1403 ANNOTATIONHIGHLIGHT.ATTRS = {};
1405 Y.extend(ANNOTATIONHIGHLIGHT, M.assignfeedback_editpdf.annotation, {
1407 * Draw a highlight annotation
1410 * @return M.assignfeedback_editpdf.drawable
1418 drawable = new M.assignfeedback_editpdf.drawable(this.editor);
1419 bounds = new M.assignfeedback_editpdf.rect();
1420 bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y),
1421 new M.assignfeedback_editpdf.point(this.endx, this.endy)]);
1423 highlightcolour = ANNOTATIONCOLOUR[this.colour];
1425 // Add an alpha channel to the rgb colour.
1427 highlightcolour = highlightcolour.replace('rgb', 'rgba');
1428 highlightcolour = highlightcolour.replace(')', ',0.5)');
1430 shape = this.editor.graphic.addShape({
1432 width: bounds.width,
1433 height: bounds.height,
1436 color: highlightcolour
1442 drawable.shapes.push(shape);
1443 this.drawable = drawable;
1445 return ANNOTATIONHIGHLIGHT.superclass.draw.apply(this);
1449 * Draw the in progress edit.
1452 * @method draw_current_edit
1453 * @param M.assignfeedback_editpdf.edit edit
1455 draw_current_edit: function(edit) {
1456 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
1461 bounds = new M.assignfeedback_editpdf.rect();
1462 bounds.bound([new M.assignfeedback_editpdf.point(edit.start.x, edit.start.y),
1463 new M.assignfeedback_editpdf.point(edit.end.x, edit.end.y)]);
1465 // Set min. width of highlight.
1466 if (!bounds.has_min_width()) {
1467 bounds.set_min_width();
1470 highlightcolour = ANNOTATIONCOLOUR[edit.annotationcolour];
1471 // Add an alpha channel to the rgb colour.
1473 highlightcolour = highlightcolour.replace('rgb', 'rgba');
1474 highlightcolour = highlightcolour.replace(')', ',0.5)');
1476 // We will draw a box with the current background colour.
1477 shape = this.editor.graphic.addShape({
1479 width: bounds.width,
1483 color: highlightcolour
1489 drawable.shapes.push(shape);
1495 * Promote the current edit to a real annotation.
1498 * @method init_from_edit
1499 * @param M.assignfeedback_editpdf.edit edit
1500 * @return bool true if highlight bound is more than min width/height, else false.
1502 init_from_edit: function(edit) {
1503 var bounds = new M.assignfeedback_editpdf.rect();
1504 bounds.bound([edit.start, edit.end]);
1506 this.gradeid = this.editor.get('gradeid');
1507 this.pageno = this.editor.currentpage;
1509 this.y = edit.start.y;
1510 this.endx = bounds.x + bounds.width;
1511 this.endy = edit.start.y + 16;
1512 this.colour = edit.annotationcolour;
1515 return (bounds.has_min_width());
1520 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
1521 M.assignfeedback_editpdf.annotationhighlight = ANNOTATIONHIGHLIGHT;
1522 // This file is part of Moodle - http://moodle.org/
1524 // Moodle is free software: you can redistribute it and/or modify
1525 // it under the terms of the GNU General Public License as published by
1526 // the Free Software Foundation, either version 3 of the License, or
1527 // (at your option) any later version.
1529 // Moodle is distributed in the hope that it will be useful,
1530 // but WITHOUT ANY WARRANTY; without even the implied warranty of
1531 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1532 // GNU General Public License for more details.
1534 // You should have received a copy of the GNU General Public License
1535 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
1536 /* global SELECTOR */
1539 * Provides an in browser PDF editor.
1541 * @module moodle-assignfeedback_editpdf-editor
1545 * Class representing a stamp.
1547 * @namespace M.assignfeedback_editpdf
1548 * @class annotationstamp
1549 * @extends M.assignfeedback_editpdf.annotation
1551 var ANNOTATIONSTAMP = function(config) {
1552 ANNOTATIONSTAMP.superclass.constructor.apply(this, [config]);
1555 ANNOTATIONSTAMP.NAME = "annotationstamp";
1556 ANNOTATIONSTAMP.ATTRS = {};
1558 Y.extend(ANNOTATIONSTAMP, M.assignfeedback_editpdf.annotation, {
1560 * Draw a stamp annotation
1563 * @return M.assignfeedback_editpdf.drawable
1566 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
1567 drawingregion = this.editor.get_dialogue_element(SELECTOR.DRAWINGREGION),
1571 position = this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(this.x, this.y));
1572 node = Y.Node.create('<div/>');
1574 'position': 'absolute',
1575 'display': 'inline-block',
1576 'backgroundImage': 'url(' + this.editor.get_stamp_image_url(this.path) + ')',
1577 'width': (this.endx - this.x),
1578 'height': (this.endy - this.y),
1579 'backgroundSize': '100% 100%',
1583 drawingregion.append(node);
1584 node.setX(position.x);
1585 node.setY(position.y);
1586 drawable.store_position(node, position.x, position.y);
1588 // Bind events only when editing.
1589 if (!this.editor.get('readonly')) {
1590 // Pass through the event handlers on the div.
1591 node.on('gesturemovestart', this.editor.edit_start, null, this.editor);
1592 node.on('gesturemove', this.editor.edit_move, null, this.editor);
1593 node.on('gesturemoveend', this.editor.edit_end, null, this.editor);
1596 drawable.nodes.push(node);
1598 this.drawable = drawable;
1599 return ANNOTATIONSTAMP.superclass.draw.apply(this);
1603 * Draw the in progress edit.
1606 * @method draw_current_edit
1607 * @param M.assignfeedback_editpdf.edit edit
1609 draw_current_edit: function(edit) {
1610 var bounds = new M.assignfeedback_editpdf.rect(),
1611 drawable = new M.assignfeedback_editpdf.drawable(this.editor),
1612 drawingregion = this.editor.get_dialogue_element(SELECTOR.DRAWINGREGION),
1616 bounds.bound([edit.start, edit.end]);
1617 position = this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(bounds.x, bounds.y));
1619 node = Y.Node.create('<div/>');
1621 'position': 'absolute',
1622 'display': 'inline-block',
1623 'backgroundImage': 'url(' + this.editor.get_stamp_image_url(edit.stamp) + ')',
1624 'width': bounds.width,
1625 'height': bounds.height,
1626 'backgroundSize': '100% 100%',
1630 drawingregion.append(node);
1631 node.setX(position.x);
1632 node.setY(position.y);
1633 drawable.store_position(node, position.x, position.y);
1635 drawable.nodes.push(node);
1641 * Promote the current edit to a real annotation.
1644 * @method init_from_edit
1645 * @param M.assignfeedback_editpdf.edit edit
1646 * @return bool if width/height is more than min. required.
1648 init_from_edit: function(edit) {
1649 var bounds = new M.assignfeedback_editpdf.rect();
1650 bounds.bound([edit.start, edit.end]);
1652 if (bounds.width < 40) {
1655 if (bounds.height < 40) {
1658 this.gradeid = this.editor.get('gradeid');
1659 this.pageno = this.editor.currentpage;
1662 this.endx = bounds.x + bounds.width;
1663 this.endy = bounds.y + bounds.height;
1664 this.colour = edit.annotationcolour;
1665 this.path = edit.stamp;
1667 // Min width and height is always more than 40px.
1672 * Move an annotation to a new location.
1676 * @method move_annotation
1678 move: function(newx, newy) {
1679 var diffx = newx - this.x,
1680 diffy = newy - this.y;
1687 if (this.drawable) {
1688 this.drawable.erase();
1690 this.editor.drawables.push(this.draw());
1695 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
1696 M.assignfeedback_editpdf.annotationstamp = ANNOTATIONSTAMP;
1697 var DROPDOWN_NAME = "Dropdown menu",
1701 * Provides an in browser PDF editor.
1703 * @module moodle-assignfeedback_editpdf-editor
1707 * This is a drop down list of buttons triggered (and aligned to) a button.
1709 * @namespace M.assignfeedback_editpdf
1712 * @extends M.core.dialogue
1714 DROPDOWN = function(config) {
1715 config.draggable = false;
1716 config.centered = false;
1717 config.width = 'auto';
1718 config.visible = false;
1719 config.footerContent = '';
1720 DROPDOWN.superclass.constructor.apply(this, [config]);
1723 Y.extend(DROPDOWN, M.core.dialogue, {
1725 * Initialise the menu.
1727 * @method initializer
1730 initializer: function(config) {
1731 var button, body, headertext, bb;
1732 DROPDOWN.superclass.initializer.call(this, config);
1734 bb = this.get('boundingBox');
1735 bb.addClass('assignfeedback_editpdf_dropdown');
1737 // Align the menu to the button that opens it.
1738 button = this.get('buttonNode');
1740 // Close the menu when clicked outside (excluding the button that opened the menu).
1741 body = this.bodyNode;
1743 headertext = Y.Node.create('<h3/>');
1744 headertext.addClass('accesshide');
1745 headertext.setHTML(this.get('headerText'));
1746 body.prepend(headertext);
1748 body.on('clickoutside', function(e) {
1749 if (this.get('visible')) {
1750 // Note: we need to compare ids because for some reason - sometimes button is an Object, not a Y.Node.
1751 if (e.target.get('id') !== button.get('id') && e.target.ancestor().get('id') !== button.get('id')) {
1758 button.on('click', function(e) {
1759 e.preventDefault(); this.show();
1761 button.on('key', this.show, 'enter,space', this);
1765 * Override the show method to align to the button.
1771 var button = this.get('buttonNode'),
1772 result = DROPDOWN.superclass.show.call(this);
1773 this.align(button, [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]);
1778 NAME: DROPDOWN_NAME,
1781 * The header for the drop down (only accessible to screen readers).
1783 * @attribute headerText
1792 * The button used to show/hide this drop down menu.
1794 * @attribute buttonNode
1804 Y.Base.modifyAttrs(DROPDOWN, {
1806 * Whether the widget should be modal or not.
1808 * Moodle override: We override this for commentsearch to force it always false.
1815 getter: function() {
1821 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
1822 M.assignfeedback_editpdf.dropdown = DROPDOWN;
1823 var COLOURPICKER_NAME = "Colourpicker",
1827 * Provides an in browser PDF editor.
1829 * @module moodle-assignfeedback_editpdf-editor
1834 * This is a drop down list of colours.
1836 * @namespace M.assignfeedback_editpdf
1837 * @class colourpicker
1839 * @extends M.assignfeedback_editpdf.dropdown
1841 COLOURPICKER = function(config) {
1842 COLOURPICKER.superclass.constructor.apply(this, [config]);
1845 Y.extend(COLOURPICKER, M.assignfeedback_editpdf.dropdown, {
1848 * Initialise the menu.
1850 * @method initializer
1853 initializer: function(config) {
1854 var colourlist = Y.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>'),
1857 // Build a list of coloured buttons.
1858 Y.each(this.get('colours'), function(rgb, colour) {
1859 var button, listitem, title, img, iconname;
1861 title = M.util.get_string(colour, 'assignfeedback_editpdf');
1862 iconname = this.get('iconprefix') + colour;
1863 img = M.util.image_url(iconname, 'assignfeedback_editpdf');
1864 button = Y.Node.create('<button><img alt="' + title + '" src="' + img + '"/></button>');
1865 button.setAttribute('data-colour', colour);
1866 button.setAttribute('data-rgb', rgb);
1867 button.setStyle('backgroundImage', 'none');
1868 listitem = Y.Node.create('<li/>');
1869 listitem.append(button);
1870 colourlist.append(listitem);
1873 body = Y.Node.create('<div/>');
1875 // Set the call back.
1876 colourlist.delegate('click', this.callback_handler, 'button', this);
1877 colourlist.delegate('key', this.callback_handler, 'down:13', 'button', this);
1879 // Set the accessible header text.
1880 this.set('headerText', M.util.get_string('colourpicker', 'assignfeedback_editpdf'));
1882 // Set the body content.
1883 body.append(colourlist);
1884 this.set('bodyContent', body);
1886 COLOURPICKER.superclass.initializer.call(this, config);
1888 callback_handler: function(e) {
1891 var callback = this.get('callback'),
1892 callbackcontext = this.get('context'),
1897 // Call the callback with the specified context.
1898 bind = Y.bind(callback, callbackcontext, e);
1903 NAME: COLOURPICKER_NAME,
1906 * The list of colours this colour picker supports.
1908 * @attribute colours
1909 * @type {String: String} (The keys of the array are the colour names and the values are localized strings)
1917 * The function called when a new colour is chosen.
1919 * @attribute callback
1928 * The context passed to the callback when a colour is chosen.
1930 * @attribute context
1939 * The prefix for the icon image names.
1941 * @attribute iconprefix
1943 * @default 'colour_'
1951 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
1952 M.assignfeedback_editpdf.colourpicker = COLOURPICKER;
1953 var STAMPPICKER_NAME = "Colourpicker",
1957 * Provides an in browser PDF editor.
1959 * @module moodle-assignfeedback_editpdf-editor
1963 * This is a drop down list of stamps.
1965 * @namespace M.assignfeedback_editpdf
1966 * @class stamppicker
1968 * @extends M.assignfeedback_editpdf.dropdown
1970 STAMPPICKER = function(config) {
1971 STAMPPICKER.superclass.constructor.apply(this, [config]);
1974 Y.extend(STAMPPICKER, M.assignfeedback_editpdf.dropdown, {
1977 * Initialise the menu.
1979 * @method initializer
1982 initializer: function(config) {
1983 var stamplist = Y.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>');
1985 // Build a list of stamped buttons.
1986 Y.each(this.get('stamps'), function(stamp) {
1987 var button, listitem, title;
1989 title = M.util.get_string('stamp', 'assignfeedback_editpdf');
1990 button = Y.Node.create('<button><img height="16" width="16" alt="' + title + '" src="' + stamp + '"/></button>');
1991 button.setAttribute('data-stamp', stamp);
1992 button.setStyle('backgroundImage', 'none');
1993 listitem = Y.Node.create('<li/>');
1994 listitem.append(button);
1995 stamplist.append(listitem);
1999 // Set the call back.
2000 stamplist.delegate('click', this.callback_handler, 'button', this);
2001 stamplist.delegate('key', this.callback_handler, 'down:13', 'button', this);
2003 // Set the accessible header text.
2004 this.set('headerText', M.util.get_string('stamppicker', 'assignfeedback_editpdf'));
2006 // Set the body content.
2007 this.set('bodyContent', stamplist);
2009 STAMPPICKER.superclass.initializer.call(this, config);
2011 callback_handler: function(e) {
2013 var callback = this.get('callback'),
2014 callbackcontext = this.get('context'),
2019 // Call the callback with the specified context.
2020 bind = Y.bind(callback, callbackcontext, e);
2025 NAME: STAMPPICKER_NAME,
2028 * The list of stamps this stamp picker supports.
2031 * @type String[] - the stamp filenames.
2039 * The function called when a new stamp is chosen.
2041 * @attribute callback
2050 * The context passed to the callback when a stamp is chosen.
2052 * @attribute context
2062 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
2063 M.assignfeedback_editpdf.stamppicker = STAMPPICKER;
2064 var COMMENTMENUNAME = "Commentmenu",
2068 * Provides an in browser PDF editor.
2070 * @module moodle-assignfeedback_editpdf-editor
2075 * This is a drop down list of comment context functions.
2077 * @namespace M.assignfeedback_editpdf
2078 * @class commentmenu
2080 * @extends M.assignfeedback_editpdf.dropdown
2082 COMMENTMENU = function(config) {
2083 COMMENTMENU.superclass.constructor.apply(this, [config]);
2086 Y.extend(COMMENTMENU, M.assignfeedback_editpdf.dropdown, {
2089 * Initialise the menu.
2091 * @method initializer
2094 initializer: function(config) {
2100 comment = this.get('comment');
2101 // Build the list of menu items.
2102 commentlinks = Y.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>');
2104 link = Y.Node.create('<li><a tabindex="-1" href="#">' +
2105 M.util.get_string('addtoquicklist', 'assignfeedback_editpdf') +
2107 link.on('click', comment.add_to_quicklist, comment);
2108 link.on('key', comment.add_to_quicklist, 'enter,space', comment);
2110 commentlinks.append(link);
2112 link = Y.Node.create('<li><a tabindex="-1" href="#">' +
2113 M.util.get_string('deletecomment', 'assignfeedback_editpdf') +
2115 link.on('click', function(e) {
2121 link.on('key', function() {
2122 comment.menu.hide();
2124 }, 'enter,space', comment);
2126 commentlinks.append(link);
2128 link = Y.Node.create('<li><hr/></li>');
2129 commentlinks.append(link);
2131 // Set the accessible header text.
2132 this.set('headerText', M.util.get_string('commentcontextmenu', 'assignfeedback_editpdf'));
2134 body = Y.Node.create('<div/>');
2136 // Set the body content.
2137 body.append(commentlinks);
2138 this.set('bodyContent', body);
2140 COMMENTMENU.superclass.initializer.call(this, config);
2150 var commentlinks = this.get('boundingBox').one('ul');
2151 commentlinks.all('.quicklist_comment').remove(true);
2152 var comment = this.get('comment');
2154 comment.deleteme = false; // Cancel the deleting of blank comments.
2156 // Now build the list of quicklist comments.
2157 Y.each(comment.editor.quicklist.comments, function(quickcomment) {
2158 var listitem = Y.Node.create('<li class="quicklist_comment"></li>'),
2159 linkitem = Y.Node.create('<a href="#" tabindex="-1">' + quickcomment.rawtext + '</a>'),
2160 deletelinkitem = Y.Node.create('<a href="#" tabindex="-1" class="delete_quicklist_comment">' +
2161 '<img src="' + M.util.image_url('t/delete', 'core') + '" ' +
2162 'alt="' + M.util.get_string('deletecomment', 'assignfeedback_editpdf') + '"/>' +
2164 listitem.append(linkitem);
2165 listitem.append(deletelinkitem);
2167 commentlinks.append(listitem);
2169 linkitem.on('click', comment.set_from_quick_comment, comment, quickcomment);
2170 linkitem.on('key', comment.set_from_quick_comment, 'space,enter', comment, quickcomment);
2172 deletelinkitem.on('click', comment.remove_from_quicklist, comment, quickcomment);
2173 deletelinkitem.on('key', comment.remove_from_quicklist, 'space,enter', comment, quickcomment);
2176 COMMENTMENU.superclass.show.call(this);
2179 NAME: COMMENTMENUNAME,
2182 * The comment this menu is attached to.
2184 * @attribute comment
2185 * @type M.assignfeedback_editpdf.comment
2195 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
2196 M.assignfeedback_editpdf.commentmenu = COMMENTMENU;
2197 /* eslint-disable no-unused-vars */
2198 /* global SELECTOR */
2199 var COMMENTSEARCHNAME = "commentsearch",
2203 * Provides an in browser PDF editor.
2205 * @module moodle-assignfeedback_editpdf-editor
2209 * This is a searchable dialogue of comments.
2211 * @namespace M.assignfeedback_editpdf
2212 * @class commentsearch
2214 * @extends M.core.dialogue
2216 COMMENTSEARCH = function(config) {
2217 config.draggable = false;
2218 config.centered = true;
2219 config.width = '400px';
2220 config.visible = false;
2221 config.headerContent = M.util.get_string('searchcomments', 'assignfeedback_editpdf');
2222 config.footerContent = '';
2223 COMMENTSEARCH.superclass.constructor.apply(this, [config]);
2226 Y.extend(COMMENTSEARCH, M.core.dialogue, {
2228 * Initialise the menu.
2230 * @method initializer
2233 initializer: function(config) {
2241 bb = this.get('boundingBox');
2242 bb.addClass('assignfeedback_editpdf_commentsearch');
2244 editor = this.get('editor');
2245 container = Y.Node.create('<div/>');
2247 placeholder = M.util.get_string('filter', 'assignfeedback_editpdf');
2248 commentfilter = Y.Node.create('<input type="text" size="20" placeholder="' + placeholder + '"/>');
2249 container.append(commentfilter);
2250 commentlist = Y.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>');
2251 container.append(commentlist);
2253 commentfilter.on('keyup', this.filter_search_comments, this);
2254 commentlist.delegate('click', this.focus_on_comment, 'a', this);
2255 commentlist.delegate('key', this.focus_on_comment, 'enter,space', 'a', this);
2257 // Set the body content.
2258 this.set('bodyContent', container);
2260 COMMENTSEARCH.superclass.initializer.call(this, config);
2264 * Event handler to filter the list of comments.
2267 * @method filter_search_comments
2269 filter_search_comments: function() {
2275 dialogueid = this.get('id');
2276 filternode = Y.one('#' + dialogueid + SELECTOR.SEARCHFILTER);
2277 commentslist = Y.one('#' + dialogueid + SELECTOR.SEARCHCOMMENTSLIST);
2279 filtertext = filternode.get('value');
2281 commentslist.all('li').each(function(node) {
2282 if (node.get('text').indexOf(filtertext) !== -1) {
2291 * Event handler to focus on a selected comment.
2295 * @method focus_on_comment
2297 focus_on_comment: function(e) {
2299 var target = e.target.ancestor('li'),
2300 comment = target.getData('comment'),
2301 editor = this.get('editor');
2305 if (comment.pageno === editor.currentpage) {
2306 comment.drawable.nodes[0].one('textarea').focus();
2308 // Comment is on a different page.
2309 editor.currentpage = comment.pageno;
2310 editor.change_page();
2311 comment.drawable.nodes[0].one('textarea').focus();
2322 var commentlist = this.get('boundingBox').one('ul'),
2323 editor = this.get('editor');
2325 commentlist.all('li').remove(true);
2327 // Rebuild the latest list of comments.
2328 Y.each(editor.pages, function(page) {
2329 Y.each(page.comments, function(comment) {
2330 var commentnode = Y.Node.create('<li><a href="#" tabindex="-1"><pre>' + comment.rawtext + '</pre></a></li>');
2331 commentlist.append(commentnode);
2332 commentnode.setData('comment', comment);
2336 this.centerDialogue();
2337 COMMENTSEARCH.superclass.show.call(this);
2340 NAME: COMMENTSEARCHNAME,
2343 * The editor this search window is attached to.
2346 * @type M.assignfeedback_editpdf.editor
2356 Y.Base.modifyAttrs(COMMENTSEARCH, {
2358 * Whether the widget should be modal or not.
2360 * Moodle override: We override this for commentsearch to force it always true.
2367 getter: function() {
2373 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
2374 M.assignfeedback_editpdf.commentsearch = COMMENTSEARCH;
2375 // This file is part of Moodle - http://moodle.org/
2377 // Moodle is free software: you can redistribute it and/or modify
2378 // it under the terms of the GNU General Public License as published by
2379 // the Free Software Foundation, either version 3 of the License, or
2380 // (at your option) any later version.
2382 // Moodle is distributed in the hope that it will be useful,
2383 // but WITHOUT ANY WARRANTY; without even the implied warranty of
2384 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2385 // GNU General Public License for more details.
2387 // You should have received a copy of the GNU General Public License
2388 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
2389 /* global SELECTOR, COMMENTCOLOUR, COMMENTTEXTCOLOUR */
2392 * Provides an in browser PDF editor.
2394 * @module moodle-assignfeedback_editpdf-editor
2398 * Class representing a list of comments.
2400 * @namespace M.assignfeedback_editpdf
2402 * @param M.assignfeedback_editpdf.editor editor
2403 * @param Int gradeid
2408 * @param String colour
2409 * @param String rawtext
2411 var COMMENT = function(editor, gradeid, pageno, x, y, width, colour, rawtext) {
2414 * Reference to M.assignfeedback_editpdf.editor.
2416 * @type M.assignfeedback_editpdf.editor
2419 this.editor = editor;
2427 this.gradeid = gradeid || 0;
2435 this.x = parseInt(x, 10) || 0;
2443 this.y = parseInt(y, 10) || 0;
2451 this.width = parseInt(width, 10) || 0;
2459 this.rawtext = rawtext || '';
2462 * Comment page number
2467 this.pageno = pageno || 0;
2470 * Comment background colour.
2475 this.colour = colour || 'yellow';
2478 * Reference to M.assignfeedback_editpdf.drawable
2479 * @property drawable
2480 * @type M.assignfeedback_editpdf.drawable
2483 this.drawable = false;
2486 * Boolean used by a timeout to delete empty comments after a short delay.
2487 * @property deleteme
2491 this.deleteme = false;
2494 * Reference to the link that opens the menu.
2495 * @property menulink
2499 this.menulink = null;
2502 * Reference to the dialogue that is the context menu.
2504 * @type M.assignfeedback_editpdf.dropdown
2510 * Clean a comment record, returning an oject with only fields that are valid.
2515 this.clean = function() {
2517 gradeid: this.gradeid,
2518 x: parseInt(this.x, 10),
2519 y: parseInt(this.y, 10),
2520 width: parseInt(this.width, 10),
2521 rawtext: this.rawtext,
2522 pageno: this.currentpage,
2530 * @method draw_comment
2531 * @param boolean focus - Set the keyboard focus to the new comment if true
2532 * @return M.assignfeedback_editpdf.drawable
2534 this.draw = function(focus) {
2535 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
2537 drawingregion = this.editor.get_dialogue_element(SELECTOR.DRAWINGREGION),
2543 // Lets add a contenteditable div.
2544 node = Y.Node.create('<textarea/>');
2545 container = Y.Node.create('<div class="commentdrawable"/>');
2546 menu = Y.Node.create('<a href="#"><img src="' + M.util.image_url('t/contextmenu', 'core') + '"/></a>');
2548 this.menulink = menu;
2549 container.append(node);
2551 if (!this.editor.get('readonly')) {
2552 container.append(menu);
2554 node.setAttribute('readonly', 'readonly');
2556 if (this.width < 100) {
2560 position = this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(this.x, this.y));
2562 width: this.width + 'px',
2563 backgroundColor: COMMENTCOLOUR[this.colour],
2564 color: COMMENTTEXTCOLOUR
2567 drawingregion.append(container);
2568 container.setStyle('position', 'absolute');
2569 container.setX(position.x);
2570 container.setY(position.y);
2571 drawable.store_position(container, position.x, position.y);
2572 drawable.nodes.push(container);
2573 node.set('value', this.rawtext);
2574 scrollheight = node.get('scrollHeight');
2576 'height': scrollheight + 'px',
2577 'overflow': 'hidden'
2579 if (!this.editor.get('readonly')) {
2580 this.attach_events(node, menu);
2585 this.drawable = drawable;
2592 * Delete an empty comment if it's menu hasn't been opened in time.
2593 * @method delete_comment_later
2595 this.delete_comment_later = function() {
2596 if (this.deleteme) {
2602 * Comment nodes have a bunch of event handlers attached to them directly.
2603 * This is all done here for neatness.
2606 * @method attach_comment_events
2607 * @param node - The Y.Node representing the comment.
2608 * @param menu - The Y.Node representing the menu.
2610 this.attach_events = function(node, menu) {
2611 // Save the text on blur.
2612 node.on('blur', function() {
2613 // Save the changes back to the comment.
2614 this.rawtext = node.get('value');
2615 this.width = parseInt(node.getStyle('width'), 10);
2618 if (this.rawtext.replace(/^\s+|\s+$/g, "") === '') {
2619 // Delete empty comments.
2620 this.deleteme = true;
2621 Y.later(400, this, this.delete_comment_later);
2623 this.editor.save_current_page();
2624 this.editor.editingcomment = false;
2627 // For delegated event handler.
2628 menu.setData('comment', this);
2630 node.on('keyup', function() {
2631 var scrollheight = node.get('scrollHeight'),
2632 height = parseInt(node.getStyle('height'), 10);
2634 // Webkit scrollheight fix.
2635 if (scrollheight === height + 8) {
2638 node.setStyle('height', scrollheight + 'px');
2642 node.on('gesturemovestart', function(e) {
2643 if (editor.currentedit.tool === 'select') {
2645 node.setData('dragging', true);
2646 node.setData('offsetx', e.clientX - node.getX());
2647 node.setData('offsety', e.clientY - node.getY());
2650 node.on('gesturemoveend', function() {
2651 if (editor.currentedit.tool === 'select') {
2652 node.setData('dragging', false);
2653 this.editor.save_current_page();
2656 node.on('gesturemove', function(e) {
2657 if (editor.currentedit.tool === 'select') {
2658 var x = e.clientX - node.getData('offsetx'),
2659 y = e.clientY - node.getData('offsety'),
2666 nodewidth = parseInt(node.getStyle('width'), 10);
2667 nodeheight = parseInt(node.getStyle('height'), 10);
2669 newlocation = this.editor.get_canvas_coordinates(new M.assignfeedback_editpdf.point(x, y));
2670 bounds = this.editor.get_canvas_bounds(true);
2674 bounds.width -= nodewidth + 42;
2675 bounds.height -= nodeheight + 8;
2676 // Clip to the window size - the comment size.
2677 newlocation.clip(bounds);
2679 this.x = newlocation.x;
2680 this.y = newlocation.y;
2682 windowlocation = this.editor.get_window_coordinates(newlocation);
2683 node.ancestor().setX(windowlocation.x);
2684 node.ancestor().setY(windowlocation.y);
2685 this.drawable.store_position(node.ancestor(), windowlocation.x, windowlocation.y);
2689 this.menu = new M.assignfeedback_editpdf.commentmenu({
2690 buttonNode: this.menulink,
2699 this.remove = function() {
2703 comments = this.editor.pages[this.editor.currentpage].comments;
2704 for (i = 0; i < comments.length; i++) {
2705 if (comments[i] === this) {
2706 comments.splice(i, 1);
2707 this.drawable.erase();
2708 this.editor.save_current_page();
2715 * Event handler to remove a comment from the users quicklist.
2718 * @method remove_from_quicklist
2720 this.remove_from_quicklist = function(e, quickcomment) {
2725 this.editor.quicklist.remove(quickcomment);
2729 * A quick comment was selected in the list, update the active comment and redraw the page.
2733 * @method set_from_quick_comment
2735 this.set_from_quick_comment = function(e, quickcomment) {
2740 this.rawtext = quickcomment.rawtext;
2741 this.width = quickcomment.width;
2742 this.colour = quickcomment.colour;
2744 this.editor.save_current_page();
2746 this.editor.redraw();
2750 * Event handler to add a comment to the users quicklist.
2753 * @method add_to_quicklist
2755 this.add_to_quicklist = function(e) {
2758 this.editor.quicklist.add(this);
2762 * Draw the in progress edit.
2765 * @method draw_current_edit
2766 * @param M.assignfeedback_editpdf.edit edit
2768 this.draw_current_edit = function(edit) {
2769 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
2773 bounds = new M.assignfeedback_editpdf.rect();
2774 bounds.bound([edit.start, edit.end]);
2776 // We will draw a box with the current background colour.
2777 shape = this.editor.graphic.addShape({
2779 width: bounds.width,