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 EXPCOLCOMMENTSBUTTON: '.expcolcommentsbutton',
34 SEARCHFILTER: '.assignfeedback_editpdf_commentsearch input',
35 SEARCHCOMMENTSLIST: '.assignfeedback_editpdf_commentsearch ul',
36 PAGESELECT: '.navigate-page-select',
37 LOADINGICON: '.loading',
38 PROGRESSBARCONTAINER: '.progress-info.progress-striped',
39 DRAWINGREGION: '.drawingregion',
40 DRAWINGCANVAS: '.drawingcanvas',
42 COMMENTCOLOURBUTTON: '.commentcolourbutton',
43 COMMENTMENU: '.commentdrawable a',
44 ANNOTATIONCOLOURBUTTON: '.annotationcolourbutton',
45 DELETEANNOTATIONBUTTON: '.deleteannotationbutton',
46 WARNINGMESSAGECONTAINER: '.warningmessages',
47 ICONMESSAGECONTAINER: '.infoicon',
48 UNSAVEDCHANGESDIV: '.assignfeedback_editpdf_warningmessages',
49 UNSAVEDCHANGESINPUT: 'input[name="assignfeedback_editpdf_haschanges"]',
50 STAMPSBUTTON: '.currentstampbutton',
51 USERINFOREGION: '[data-region="user-info"]',
52 ROTATELEFTBUTTON: '.rotateleftbutton',
53 ROTATERIGHTBUTTON: '.rotaterightbutton',
54 DIALOGUE: '.' + CSS.DIALOGUE
56 SELECTEDBORDERCOLOUR = 'rgba(200, 200, 255, 0.9)',
57 SELECTEDFILLCOLOUR = 'rgba(200, 200, 255, 0.5)',
58 COMMENTTEXTCOLOUR = 'rgb(51, 51, 51)',
60 'white': 'rgb(255,255,255)',
61 'yellow': 'rgb(255,236,174)',
62 'red': 'rgb(249,181,179)',
63 'green': 'rgb(214,234,178)',
64 'blue': 'rgb(203,217,237)',
65 'clear': 'rgba(255,255,255, 0)'
68 'white': 'rgb(255,255,255)',
69 'yellow': 'rgb(255,207,53)',
70 'red': 'rgb(239,69,64)',
71 'green': 'rgb(152,202,62)',
72 'blue': 'rgb(125,159,211)',
73 'black': 'rgb(51,51,51)'
77 'comment': '.commentbutton',
79 'line': '.linebutton',
80 'rectangle': '.rectanglebutton',
81 'oval': '.ovalbutton',
82 'stamp': '.stampbutton',
83 'select': '.selectbutton',
84 'drag': '.dragbutton',
85 'highlight': '.highlightbutton'
88 // This file is part of Moodle - http://moodle.org/
90 // Moodle is free software: you can redistribute it and/or modify
91 // it under the terms of the GNU General Public License as published by
92 // the Free Software Foundation, either version 3 of the License, or
93 // (at your option) any later version.
95 // Moodle is distributed in the hope that it will be useful,
96 // but WITHOUT ANY WARRANTY; without even the implied warranty of
97 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
98 // GNU General Public License for more details.
100 // You should have received a copy of the GNU General Public License
101 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
104 * Provides an in browser PDF editor.
106 * @module moodle-assignfeedback_editpdf-editor
110 * Class representing a 2d point.
112 * @namespace M.assignfeedback_editpdf
117 var POINT = function(x, y) {
125 this.x = parseInt(x, 10);
133 this.y = parseInt(y, 10);
136 * Clip this point to the rect
138 * @param M.assignfeedback_editpdf.point
141 this.clip = function(bounds) {
142 if (this.x < bounds.x) {
145 if (this.x > (bounds.x + bounds.width)) {
146 this.x = bounds.x + bounds.width;
148 if (this.y < bounds.y) {
151 if (this.y > (bounds.y + bounds.height)) {
152 this.y = bounds.y + bounds.height;
159 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
160 M.assignfeedback_editpdf.point = POINT;
161 // This file is part of Moodle - http://moodle.org/
163 // Moodle is free software: you can redistribute it and/or modify
164 // it under the terms of the GNU General Public License as published by
165 // the Free Software Foundation, either version 3 of the License, or
166 // (at your option) any later version.
168 // Moodle is distributed in the hope that it will be useful,
169 // but WITHOUT ANY WARRANTY; without even the implied warranty of
170 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
171 // GNU General Public License for more details.
173 // You should have received a copy of the GNU General Public License
174 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
177 * Provides an in browser PDF editor.
179 * @module moodle-assignfeedback_editpdf-editor
183 * Class representing a 2d rect.
185 * @namespace M.assignfeedback_editpdf
192 var RECT = function(x, y, width, height) {
224 this.height = height;
227 * Set this rect to represent the smallest possible rectangle containing this list of points.
229 * @param M.assignfeedback_editpdf.point[]
232 this.bound = function(points) {
240 for (i = 0; i < points.length; i++) {
242 if (point.x < minx || i === 0) {
245 if (point.x > maxx || i === 0) {
248 if (point.y < miny || i === 0) {
251 if (point.y > maxy || i === 0) {
257 this.width = maxx - minx;
258 this.height = maxy - miny;
264 * Checks if rect has min width.
265 * @method has_min_width
266 * @return bool true if width is more than 5px.
269 this.has_min_width = function() {
270 return (this.width >= 5);
274 * Checks if rect has min height.
275 * @method has_min_height
276 * @return bool true if height is more than 5px.
279 this.has_min_height = function() {
280 return (this.height >= 5);
284 * Set min. width of annotation bound.
285 * @method set_min_width
288 this.set_min_width = function() {
293 * Set min. height of annotation bound.
294 * @method set_min_height
297 this.set_min_height = function() {
302 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
303 M.assignfeedback_editpdf.rect = RECT;
304 // This file is part of Moodle - http://moodle.org/
306 // Moodle is free software: you can redistribute it and/or modify
307 // it under the terms of the GNU General Public License as published by
308 // the Free Software Foundation, either version 3 of the License, or
309 // (at your option) any later version.
311 // Moodle is distributed in the hope that it will be useful,
312 // but WITHOUT ANY WARRANTY; without even the implied warranty of
313 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
314 // GNU General Public License for more details.
316 // You should have received a copy of the GNU General Public License
317 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
320 * Provides an in browser PDF editor.
322 * @module moodle-assignfeedback_editpdf-editor
328 * @namespace M.assignfeedback_editpdf
331 var EDIT = function() {
334 * Starting point for the edit.
336 * @type M.assignfeedback_editpdf.point|false
342 * Finishing point for the edit.
344 * @type M.assignfeedback_editpdf.point|false
350 * Starting time for the edit.
351 * @property starttime
358 * Starting point for the currently selected annotation.
359 * @property annotationstart
360 * @type M.assignfeedback_editpdf.point|false
363 this.annotationstart = false;
366 * The currently selected tool
374 * The currently comment colour
375 * @property commentcolour
379 this.commentcolour = 'yellow';
382 * The currently annotation colour
383 * @property annotationcolour
387 this.annotationcolour = 'red';
390 * The current stamp image.
398 * List of points the the current drawing path.
400 * @type M.assignfeedback_editpdf.point[]
406 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
407 M.assignfeedback_editpdf.edit = EDIT;
408 // This file is part of Moodle - http://moodle.org/
410 // Moodle is free software: you can redistribute it and/or modify
411 // it under the terms of the GNU General Public License as published by
412 // the Free Software Foundation, either version 3 of the License, or
413 // (at your option) any later version.
415 // Moodle is distributed in the hope that it will be useful,
416 // but WITHOUT ANY WARRANTY; without even the implied warranty of
417 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
418 // GNU General Public License for more details.
420 // You should have received a copy of the GNU General Public License
421 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
424 * Provides an in browser PDF editor.
426 * @module moodle-assignfeedback_editpdf-editor
430 * Class representing a drawable thing which contains both Y.Nodes, and Y.Shapes.
432 * @namespace M.assignfeedback_editpdf
433 * @param M.assignfeedback_editpdf.editor editor
436 var DRAWABLE = function(editor) {
439 * Reference to M.assignfeedback_editpdf.editor.
441 * @type M.assignfeedback_editpdf.editor
444 this.editor = editor;
463 * Delete the shapes from the drawable.
465 * @method erase_drawable
467 this.erase = function() {
469 while (this.shapes.length > 0) {
470 this.editor.graphic.removeShape(this.shapes.pop());
474 while (this.nodes.length > 0) {
475 this.nodes.pop().remove();
481 * Update the positions of all absolutely positioned nodes, when the drawing canvas is scrolled
483 * @method scroll_update
487 this.scroll_update = function(scrollx, scrolly) {
489 for (i = 0; i < this.nodes.length; i++) {
490 x = this.nodes[i].getData('x');
491 y = this.nodes[i].getData('y');
492 if (x !== undefined && y !== undefined) {
493 this.nodes[i].setX(parseInt(x, 10) - scrollx);
494 this.nodes[i].setY(parseInt(y, 10) - scrolly);
500 * Store the initial position of the node, so it can be updated when the drawing canvas is scrolled
502 * @method store_position
507 this.store_position = function(container, x, y) {
508 var drawingregion, scrollx, scrolly;
510 drawingregion = this.editor.get_dialogue_element(SELECTOR.DRAWINGREGION);
511 scrollx = parseInt(drawingregion.get('scrollLeft'), 10);
512 scrolly = parseInt(drawingregion.get('scrollTop'), 10);
513 container.setData('x', x + scrollx);
514 container.setData('y', y + scrolly);
518 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
519 M.assignfeedback_editpdf.drawable = DRAWABLE;
520 // This file is part of Moodle - http://moodle.org/
522 // Moodle is free software: you can redistribute it and/or modify
523 // it under the terms of the GNU General Public License as published by
524 // the Free Software Foundation, either version 3 of the License, or
525 // (at your option) any later version.
527 // Moodle is distributed in the hope that it will be useful,
528 // but WITHOUT ANY WARRANTY; without even the implied warranty of
529 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
530 // GNU General Public License for more details.
532 // You should have received a copy of the GNU General Public License
533 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
536 * Provides an in browser PDF editor.
538 * @module moodle-assignfeedback_editpdf-editor
542 * Class representing a highlight.
544 * @namespace M.assignfeedback_editpdf
548 var ANNOTATION = function(config) {
549 ANNOTATION.superclass.constructor.apply(this, [config]);
552 ANNOTATION.NAME = "annotation";
553 ANNOTATION.ATTRS = {};
555 Y.extend(ANNOTATION, Y.Base, {
557 * Reference to M.assignfeedback_editpdf.editor.
559 * @type M.assignfeedback_editpdf.editor
573 * Comment page number
615 * @type String - list of points like x1,y1:x2,y2
637 * Reference to M.assignfeedback_editpdf.drawable
639 * @type M.assignfeedback_editpdf.drawable
645 * Initialise the annotation.
647 * @method initializer
650 initializer: function(config) {
651 this.editor = config.editor || null;
652 this.gradeid = parseInt(config.gradeid, 10) || 0;
653 this.pageno = parseInt(config.pageno, 10) || 0;
654 this.x = parseInt(config.x, 10) || 0;
655 this.y = parseInt(config.y, 10) || 0;
656 this.endx = parseInt(config.endx, 10) || 0;
657 this.endy = parseInt(config.endy, 10) || 0;
658 this.path = config.path || '';
659 this.type = config.type || 'rect';
660 this.colour = config.colour || 'red';
661 this.drawable = false;
665 * Clean a comment record, returning an oject with only fields that are valid.
672 gradeid: this.gradeid,
673 x: parseInt(this.x, 10),
674 y: parseInt(this.y, 10),
675 endx: parseInt(this.endx, 10),
676 endy: parseInt(this.endy, 10),
685 * Draw a selection around this annotation if it is selected.
687 * @method draw_highlight
688 * @return M.assignfeedback_editpdf.drawable
690 draw_highlight: function() {
692 drawingregion = this.editor.get_dialogue_element(SELECTOR.DRAWINGREGION),
693 offsetcanvas = this.editor.get_dialogue_element(SELECTOR.DRAWINGCANVAS).getXY(),
696 if (this.editor.currentannotation === this) {
697 // Draw a highlight around the annotation.
698 bounds = new M.assignfeedback_editpdf.rect();
699 bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y),
700 new M.assignfeedback_editpdf.point(this.endx, this.endy)]);
702 shape = this.editor.graphic.addShape({
705 height: bounds.height,
707 weight: STROKEWEIGHT,
708 color: SELECTEDBORDERCOLOUR
711 color: SELECTEDFILLCOLOUR
716 this.drawable.shapes.push(shape);
718 // Add a delete X to the annotation.
719 var deleteicon = Y.Node.create('<img src="' + M.util.image_url('trash', 'assignfeedback_editpdf') + '"/>'),
720 deletelink = Y.Node.create('<a href="#" role="button"></a>');
722 deleteicon.setAttrs({
723 'alt': M.util.get_string('deleteannotation', 'assignfeedback_editpdf')
725 deleteicon.setStyles({
726 'backgroundColor': 'white'
728 deletelink.addClass('deleteannotationbutton');
729 deletelink.append(deleteicon);
731 drawingregion.append(deletelink);
732 deletelink.setData('annotation', this);
734 deletelink.on('click', this.remove, this);
735 deletelink.on('key', this.remove, 'space,enter', this);
737 deletelink.setX(offsetcanvas[0] + bounds.x + bounds.width - 18);
738 deletelink.setY(offsetcanvas[1] + bounds.y + 6);
739 this.drawable.nodes.push(deletelink);
741 return this.drawable;
748 * @return M.assignfeedback_editpdf.drawable|false
751 // Should be overridden by the subclass.
752 this.draw_highlight();
753 return this.drawable;
757 * Delete an annotation
762 remove: function(e) {
768 annotations = this.editor.pages[this.editor.currentpage].annotations;
769 for (i = 0; i < annotations.length; i++) {
770 if (annotations[i] === this) {
771 annotations.splice(i, 1);
773 this.drawable.erase();
775 this.editor.currentannotation = false;
776 this.editor.save_current_page();
783 * Move an annotation to a new location.
787 * @method move_annotation
789 move: function(newx, newy) {
790 var diffx = newx - this.x,
791 diffy = newy - this.y,
792 newpath, oldpath, xy,
802 oldpath = this.path.split(':');
803 Y.each(oldpath, function(position) {
804 xy = position.split(',');
805 x = parseInt(xy[0], 10);
806 y = parseInt(xy[1], 10);
807 newpath.push((x + diffx) + ',' + (y + diffy));
810 this.path = newpath.join(':');
814 this.drawable.erase();
816 this.editor.drawables.push(this.draw());
820 * Draw the in progress edit.
823 * @method draw_current_edit
824 * @param M.assignfeedback_editpdf.edit edit
826 draw_current_edit: function(edit) {
827 var noop = edit && false;
828 // Override me please.
833 * Promote the current edit to a real annotation.
836 * @method init_from_edit
837 * @param M.assignfeedback_editpdf.edit edit
838 * @return bool if width/height is more than min. required.
840 init_from_edit: function(edit) {
841 var bounds = new M.assignfeedback_editpdf.rect();
842 bounds.bound([edit.start, edit.end]);
844 this.gradeid = this.editor.get('gradeid');
845 this.pageno = this.editor.currentpage;
848 this.endx = bounds.x + bounds.width;
849 this.endy = bounds.y + bounds.height;
850 this.colour = edit.annotationcolour;
852 return (bounds.has_min_width() && bounds.has_min_height());
857 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
858 M.assignfeedback_editpdf.annotation = ANNOTATION;
859 // This file is part of Moodle - http://moodle.org/
861 // Moodle is free software: you can redistribute it and/or modify
862 // it under the terms of the GNU General Public License as published by
863 // the Free Software Foundation, either version 3 of the License, or
864 // (at your option) any later version.
866 // Moodle is distributed in the hope that it will be useful,
867 // but WITHOUT ANY WARRANTY; without even the implied warranty of
868 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
869 // GNU General Public License for more details.
871 // You should have received a copy of the GNU General Public License
872 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
875 * Provides an in browser PDF editor.
877 * @module moodle-assignfeedback_editpdf-editor
881 * Class representing a line.
883 * @namespace M.assignfeedback_editpdf
884 * @class annotationline
885 * @extends M.assignfeedback_editpdf.annotation
887 var ANNOTATIONLINE = function(config) {
888 ANNOTATIONLINE.superclass.constructor.apply(this, [config]);
891 ANNOTATIONLINE.NAME = "annotationline";
892 ANNOTATIONLINE.ATTRS = {};
894 Y.extend(ANNOTATIONLINE, M.assignfeedback_editpdf.annotation, {
896 * Draw a line annotation
899 * @return M.assignfeedback_editpdf.drawable
905 drawable = new M.assignfeedback_editpdf.drawable(this.editor);
907 shape = this.editor.graphic.addShape({
911 weight: STROKEWEIGHT,
912 color: ANNOTATIONCOLOUR[this.colour]
916 shape.moveTo(this.x, this.y);
917 shape.lineTo(this.endx, this.endy);
919 drawable.shapes.push(shape);
920 this.drawable = drawable;
922 return ANNOTATIONLINE.superclass.draw.apply(this);
926 * Draw the in progress edit.
929 * @method draw_current_edit
930 * @param M.assignfeedback_editpdf.edit edit
932 draw_current_edit: function(edit) {
933 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
936 shape = this.editor.graphic.addShape({
940 weight: STROKEWEIGHT,
941 color: ANNOTATIONCOLOUR[edit.annotationcolour]
945 shape.moveTo(edit.start.x, edit.start.y);
946 shape.lineTo(edit.end.x, edit.end.y);
949 drawable.shapes.push(shape);
955 * Promote the current edit to a real annotation.
958 * @method init_from_edit
959 * @param M.assignfeedback_editpdf.edit edit
960 * @return bool true if line bound is more than min width/height, else false.
962 init_from_edit: function(edit) {
963 this.gradeid = this.editor.get('gradeid');
964 this.pageno = this.editor.currentpage;
965 this.x = edit.start.x;
966 this.y = edit.start.y;
967 this.endx = edit.end.x;
968 this.endy = edit.end.y;
969 this.colour = edit.annotationcolour;
972 return !(((this.endx - this.x) === 0) && ((this.endy - this.y) === 0));
977 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
978 M.assignfeedback_editpdf.annotationline = ANNOTATIONLINE;
979 // This file is part of Moodle - http://moodle.org/
981 // Moodle is free software: you can redistribute it and/or modify
982 // it under the terms of the GNU General Public License as published by
983 // the Free Software Foundation, either version 3 of the License, or
984 // (at your option) any later version.
986 // Moodle is distributed in the hope that it will be useful,
987 // but WITHOUT ANY WARRANTY; without even the implied warranty of
988 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
989 // GNU General Public License for more details.
991 // You should have received a copy of the GNU General Public License
992 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
995 * Provides an in browser PDF editor.
997 * @module moodle-assignfeedback_editpdf-editor
1001 * Class representing a rectangle.
1003 * @namespace M.assignfeedback_editpdf
1004 * @class annotationrectangle
1005 * @extends M.assignfeedback_editpdf.annotation
1007 var ANNOTATIONRECTANGLE = function(config) {
1008 ANNOTATIONRECTANGLE.superclass.constructor.apply(this, [config]);
1011 ANNOTATIONRECTANGLE.NAME = "annotationrectangle";
1012 ANNOTATIONRECTANGLE.ATTRS = {};
1014 Y.extend(ANNOTATIONRECTANGLE, M.assignfeedback_editpdf.annotation, {
1016 * Draw a rectangle annotation
1019 * @return M.assignfeedback_editpdf.drawable
1026 drawable = new M.assignfeedback_editpdf.drawable(this.editor);
1028 bounds = new M.assignfeedback_editpdf.rect();
1029 bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y),
1030 new M.assignfeedback_editpdf.point(this.endx, this.endy)]);
1032 shape = this.editor.graphic.addShape({
1034 width: bounds.width,
1035 height: bounds.height,
1037 weight: STROKEWEIGHT,
1038 color: ANNOTATIONCOLOUR[this.colour]
1043 drawable.shapes.push(shape);
1044 this.drawable = drawable;
1046 return ANNOTATIONRECTANGLE.superclass.draw.apply(this);
1050 * Draw the in progress edit.
1053 * @method draw_current_edit
1054 * @param M.assignfeedback_editpdf.edit edit
1056 draw_current_edit: function(edit) {
1057 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
1061 bounds = new M.assignfeedback_editpdf.rect();
1062 bounds.bound([new M.assignfeedback_editpdf.point(edit.start.x, edit.start.y),
1063 new M.assignfeedback_editpdf.point(edit.end.x, edit.end.y)]);
1065 // Set min. width and height of rectangle.
1066 if (!bounds.has_min_width()) {
1067 bounds.set_min_width();
1069 if (!bounds.has_min_height()) {
1070 bounds.set_min_height();
1073 shape = this.editor.graphic.addShape({
1075 width: bounds.width,
1076 height: bounds.height,
1078 weight: STROKEWEIGHT,
1079 color: ANNOTATIONCOLOUR[edit.annotationcolour]
1085 drawable.shapes.push(shape);
1091 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
1092 M.assignfeedback_editpdf.annotationrectangle = ANNOTATIONRECTANGLE;
1093 // This file is part of Moodle - http://moodle.org/
1095 // Moodle is free software: you can redistribute it and/or modify
1096 // it under the terms of the GNU General Public License as published by
1097 // the Free Software Foundation, either version 3 of the License, or
1098 // (at your option) any later version.
1100 // Moodle is distributed in the hope that it will be useful,
1101 // but WITHOUT ANY WARRANTY; without even the implied warranty of
1102 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1103 // GNU General Public License for more details.
1105 // You should have received a copy of the GNU General Public License
1106 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
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/>.
1223 * Provides an in browser PDF editor.
1225 * @module moodle-assignfeedback_editpdf-editor
1229 * Class representing a pen.
1231 * @namespace M.assignfeedback_editpdf
1232 * @class annotationpen
1233 * @extends M.assignfeedback_editpdf.annotation
1235 var ANNOTATIONPEN = function(config) {
1236 ANNOTATIONPEN.superclass.constructor.apply(this, [config]);
1239 ANNOTATIONPEN.NAME = "annotationpen";
1240 ANNOTATIONPEN.ATTRS = {};
1242 Y.extend(ANNOTATIONPEN, M.assignfeedback_editpdf.annotation, {
1244 * Draw a pen annotation
1247 * @return M.assignfeedback_editpdf.drawable
1256 drawable = new M.assignfeedback_editpdf.drawable(this.editor);
1258 shape = this.editor.graphic.addShape({
1262 weight: STROKEWEIGHT,
1263 color: ANNOTATIONCOLOUR[this.colour]
1268 // Recreate the pen path array.
1269 positions = this.path.split(':');
1270 // Redraw all the lines.
1271 Y.each(positions, function(position) {
1272 xy = position.split(',');
1274 shape.moveTo(xy[0], xy[1]);
1277 shape.lineTo(xy[0], xy[1]);
1283 drawable.shapes.push(shape);
1284 this.drawable = drawable;
1286 return ANNOTATIONPEN.superclass.draw.apply(this);
1290 * Draw the in progress edit.
1293 * @method draw_current_edit
1294 * @param M.assignfeedback_editpdf.edit edit
1296 draw_current_edit: function(edit) {
1297 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
1301 shape = this.editor.graphic.addShape({
1305 weight: STROKEWEIGHT,
1306 color: ANNOTATIONCOLOUR[edit.annotationcolour]
1311 // Recreate the pen path array.
1312 // Redraw all the lines.
1313 Y.each(edit.path, function(position) {
1315 shape.moveTo(position.x, position.y);
1318 shape.lineTo(position.x, position.y);
1324 drawable.shapes.push(shape);
1331 * Promote the current edit to a real annotation.
1334 * @method init_from_edit
1335 * @param M.assignfeedback_editpdf.edit edit
1336 * @return bool true if pen bound is more than min width/height, else false.
1338 init_from_edit: function(edit) {
1339 var bounds = new M.assignfeedback_editpdf.rect(),
1343 // This will get the boundaries of all points in the path.
1344 bounds.bound(edit.path);
1346 for (i = 0; i < edit.path.length; i++) {
1347 pathlist.push(parseInt(edit.path[i].x, 10) + ',' + parseInt(edit.path[i].y, 10));
1350 this.gradeid = this.editor.get('gradeid');
1351 this.pageno = this.editor.currentpage;
1354 this.endx = bounds.x + bounds.width;
1355 this.endy = bounds.y + bounds.height;
1356 this.colour = edit.annotationcolour;
1357 this.path = pathlist.join(':');
1359 return (bounds.has_min_width() || bounds.has_min_height());
1365 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
1366 M.assignfeedback_editpdf.annotationpen = ANNOTATIONPEN;
1367 // This file is part of Moodle - http://moodle.org/
1369 // Moodle is free software: you can redistribute it and/or modify
1370 // it under the terms of the GNU General Public License as published by
1371 // the Free Software Foundation, either version 3 of the License, or
1372 // (at your option) any later version.
1374 // Moodle is distributed in the hope that it will be useful,
1375 // but WITHOUT ANY WARRANTY; without even the implied warranty of
1376 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1377 // GNU General Public License for more details.
1379 // You should have received a copy of the GNU General Public License
1380 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
1383 * Provides an in browser PDF editor.
1385 * @module moodle-assignfeedback_editpdf-editor
1389 * Class representing a highlight.
1391 * @namespace M.assignfeedback_editpdf
1392 * @class annotationhighlight
1393 * @extends M.assignfeedback_editpdf.annotation
1394 * @module moodle-assignfeedback_editpdf-editor
1396 var ANNOTATIONHIGHLIGHT = function(config) {
1397 ANNOTATIONHIGHLIGHT.superclass.constructor.apply(this, [config]);
1400 ANNOTATIONHIGHLIGHT.NAME = "annotationhighlight";
1401 ANNOTATIONHIGHLIGHT.ATTRS = {};
1403 Y.extend(ANNOTATIONHIGHLIGHT, M.assignfeedback_editpdf.annotation, {
1405 * Draw a highlight annotation
1408 * @return M.assignfeedback_editpdf.drawable
1416 drawable = new M.assignfeedback_editpdf.drawable(this.editor);
1417 bounds = new M.assignfeedback_editpdf.rect();
1418 bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y),
1419 new M.assignfeedback_editpdf.point(this.endx, this.endy)]);
1421 highlightcolour = ANNOTATIONCOLOUR[this.colour];
1423 // Add an alpha channel to the rgb colour.
1425 highlightcolour = highlightcolour.replace('rgb', 'rgba');
1426 highlightcolour = highlightcolour.replace(')', ',0.5)');
1428 shape = this.editor.graphic.addShape({
1430 width: bounds.width,
1431 height: bounds.height,
1434 color: highlightcolour
1440 drawable.shapes.push(shape);
1441 this.drawable = drawable;
1443 return ANNOTATIONHIGHLIGHT.superclass.draw.apply(this);
1447 * Draw the in progress edit.
1450 * @method draw_current_edit
1451 * @param M.assignfeedback_editpdf.edit edit
1453 draw_current_edit: function(edit) {
1454 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
1459 bounds = new M.assignfeedback_editpdf.rect();
1460 bounds.bound([new M.assignfeedback_editpdf.point(edit.start.x, edit.start.y),
1461 new M.assignfeedback_editpdf.point(edit.end.x, edit.end.y)]);
1463 // Set min. width of highlight.
1464 if (!bounds.has_min_width()) {
1465 bounds.set_min_width();
1468 highlightcolour = ANNOTATIONCOLOUR[edit.annotationcolour];
1469 // Add an alpha channel to the rgb colour.
1471 highlightcolour = highlightcolour.replace('rgb', 'rgba');
1472 highlightcolour = highlightcolour.replace(')', ',0.5)');
1474 // We will draw a box with the current background colour.
1475 shape = this.editor.graphic.addShape({
1477 width: bounds.width,
1481 color: highlightcolour
1484 y: edit.start.y - 10
1487 drawable.shapes.push(shape);
1493 * Promote the current edit to a real annotation.
1496 * @method init_from_edit
1497 * @param M.assignfeedback_editpdf.edit edit
1498 * @return bool true if highlight bound is more than min width/height, else false.
1500 init_from_edit: function(edit) {
1501 var bounds = new M.assignfeedback_editpdf.rect();
1502 bounds.bound([edit.start, edit.end]);
1504 this.gradeid = this.editor.get('gradeid');
1505 this.pageno = this.editor.currentpage;
1507 this.y = edit.start.y - 10;
1508 this.endx = bounds.x + bounds.width;
1509 this.endy = edit.start.y + 10;
1510 this.colour = edit.annotationcolour;
1513 return (bounds.has_min_width());
1518 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
1519 M.assignfeedback_editpdf.annotationhighlight = ANNOTATIONHIGHLIGHT;
1520 // This file is part of Moodle - http://moodle.org/
1522 // Moodle is free software: you can redistribute it and/or modify
1523 // it under the terms of the GNU General Public License as published by
1524 // the Free Software Foundation, either version 3 of the License, or
1525 // (at your option) any later version.
1527 // Moodle is distributed in the hope that it will be useful,
1528 // but WITHOUT ANY WARRANTY; without even the implied warranty of
1529 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1530 // GNU General Public License for more details.
1532 // You should have received a copy of the GNU General Public License
1533 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
1536 * Provides an in browser PDF editor.
1538 * @module moodle-assignfeedback_editpdf-editor
1542 * Class representing a stamp.
1544 * @namespace M.assignfeedback_editpdf
1545 * @class annotationstamp
1546 * @extends M.assignfeedback_editpdf.annotation
1548 var ANNOTATIONSTAMP = function(config) {
1549 ANNOTATIONSTAMP.superclass.constructor.apply(this, [config]);
1552 ANNOTATIONSTAMP.NAME = "annotationstamp";
1553 ANNOTATIONSTAMP.ATTRS = {};
1555 Y.extend(ANNOTATIONSTAMP, M.assignfeedback_editpdf.annotation, {
1557 * Draw a stamp annotation
1560 * @return M.assignfeedback_editpdf.drawable
1563 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
1564 drawingcanvas = this.editor.get_dialogue_element(SELECTOR.DRAWINGCANVAS),
1568 position = this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(this.x, this.y));
1569 node = Y.Node.create('<div/>');
1570 node.addClass('annotation');
1571 node.addClass('stamp');
1573 'position': 'absolute',
1574 'display': 'inline-block',
1575 'backgroundImage': 'url(' + this.editor.get_stamp_image_url(this.path) + ')',
1576 'width': (this.endx - this.x),
1577 'height': (this.endy - this.y),
1578 'backgroundSize': '100% 100%'
1581 drawingcanvas.append(node);
1582 node.setX(position.x);
1583 node.setY(position.y);
1584 drawable.store_position(node, position.x, position.y);
1586 // Bind events only when editing.
1587 if (!this.editor.get('readonly')) {
1588 // Pass through the event handlers on the div.
1589 node.on('gesturemovestart', this.editor.edit_start, null, this.editor);
1590 node.on('gesturemove', this.editor.edit_move, null, this.editor);
1591 node.on('gesturemoveend', this.editor.edit_end, null, this.editor);
1594 drawable.nodes.push(node);
1596 this.drawable = drawable;
1597 return ANNOTATIONSTAMP.superclass.draw.apply(this);
1601 * Draw the in progress edit.
1604 * @method draw_current_edit
1605 * @param M.assignfeedback_editpdf.edit edit
1607 draw_current_edit: function(edit) {
1608 var bounds = new M.assignfeedback_editpdf.rect(),
1609 drawable = new M.assignfeedback_editpdf.drawable(this.editor),
1610 drawingregion = this.editor.get_dialogue_element(SELECTOR.DRAWINGREGION),
1614 bounds.bound([edit.start, edit.end]);
1615 position = this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(bounds.x, bounds.y));
1617 node = Y.Node.create('<div/>');
1618 node.addClass('annotation');
1619 node.addClass('stamp');
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%'
1629 drawingregion.append(node);
1630 node.setX(position.x);
1631 node.setY(position.y);
1632 drawable.store_position(node, position.x, position.y);
1634 drawable.nodes.push(node);
1640 * Promote the current edit to a real annotation.
1643 * @method init_from_edit
1644 * @param M.assignfeedback_editpdf.edit edit
1645 * @return bool if width/height is more than min. required.
1647 init_from_edit: function(edit) {
1648 var bounds = new M.assignfeedback_editpdf.rect();
1649 bounds.bound([edit.start, edit.end]);
1651 if (bounds.width < 40) {
1654 if (bounds.height < 40) {
1657 this.gradeid = this.editor.get('gradeid');
1658 this.pageno = this.editor.currentpage;
1661 this.endx = bounds.x + bounds.width;
1662 this.endy = bounds.y + bounds.height;
1663 this.colour = edit.annotationcolour;
1664 this.path = edit.stamp;
1666 // Min width and height is always more than 40px.
1671 * Move an annotation to a new location.
1675 * @method move_annotation
1677 move: function(newx, newy) {
1678 var diffx = newx - this.x,
1679 diffy = newy - this.y;
1686 if (this.drawable) {
1687 this.drawable.erase();
1689 this.editor.drawables.push(this.draw());
1694 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
1695 M.assignfeedback_editpdf.annotationstamp = ANNOTATIONSTAMP;
1696 var DROPDOWN_NAME = "Dropdown menu",
1700 * Provides an in browser PDF editor.
1702 * @module moodle-assignfeedback_editpdf-editor
1706 * This is a drop down list of buttons triggered (and aligned to) a button.
1708 * @namespace M.assignfeedback_editpdf
1711 * @extends M.core.dialogue
1713 DROPDOWN = function(config) {
1714 config.draggable = false;
1715 config.centered = false;
1716 config.width = 'auto';
1717 config.visible = false;
1718 config.footerContent = '';
1719 DROPDOWN.superclass.constructor.apply(this, [config]);
1722 Y.extend(DROPDOWN, M.core.dialogue, {
1724 * Initialise the menu.
1726 * @method initializer
1729 initializer: function(config) {
1730 var button, body, headertext, bb;
1731 DROPDOWN.superclass.initializer.call(this, config);
1733 bb = this.get('boundingBox');
1734 bb.addClass('assignfeedback_editpdf_dropdown');
1736 // Align the menu to the button that opens it.
1737 button = this.get('buttonNode');
1739 // Close the menu when clicked outside (excluding the button that opened the menu).
1740 body = this.bodyNode;
1742 headertext = Y.Node.create('<h3/>');
1743 headertext.addClass('accesshide');
1744 headertext.setHTML(this.get('headerText'));
1745 body.prepend(headertext);
1747 body.on('clickoutside', function(e) {
1748 if (this.get('visible')) {
1749 // Note: we need to compare ids because for some reason - sometimes button is an Object, not a Y.Node.
1750 if (e.target.get('id') !== button.get('id') && e.target.ancestor().get('id') !== button.get('id')) {
1757 button.on('click', function(e) {
1758 e.preventDefault(); this.show();
1760 button.on('key', this.show, 'enter,space', this);
1764 * Override the show method to align to the button.
1770 var button = this.get('buttonNode'),
1771 result = DROPDOWN.superclass.show.call(this);
1772 this.align(button, [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]);
1777 NAME: DROPDOWN_NAME,
1780 * The header for the drop down (only accessible to screen readers).
1782 * @attribute headerText
1791 * The button used to show/hide this drop down menu.
1793 * @attribute buttonNode
1803 Y.Base.modifyAttrs(DROPDOWN, {
1805 * Whether the widget should be modal or not.
1807 * Moodle override: We override this for commentsearch to force it always false.
1814 getter: function() {
1820 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
1821 M.assignfeedback_editpdf.dropdown = DROPDOWN;
1822 var COLOURPICKER_NAME = "Colourpicker",
1826 * Provides an in browser PDF editor.
1828 * @module moodle-assignfeedback_editpdf-editor
1833 * This is a drop down list of colours.
1835 * @namespace M.assignfeedback_editpdf
1836 * @class colourpicker
1838 * @extends M.assignfeedback_editpdf.dropdown
1840 COLOURPICKER = function(config) {
1841 COLOURPICKER.superclass.constructor.apply(this, [config]);
1844 Y.extend(COLOURPICKER, M.assignfeedback_editpdf.dropdown, {
1847 * Initialise the menu.
1849 * @method initializer
1852 initializer: function(config) {
1853 var colourlist = Y.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>'),
1856 // Build a list of coloured buttons.
1857 Y.each(this.get('colours'), function(rgb, colour) {
1858 var button, listitem, title, img, iconname;
1860 title = M.util.get_string(colour, 'assignfeedback_editpdf');
1861 iconname = this.get('iconprefix') + colour;
1862 img = M.util.image_url(iconname, 'assignfeedback_editpdf');
1863 button = Y.Node.create('<button><img alt="' + title + '" src="' + img + '"/></button>');
1864 button.setAttribute('data-colour', colour);
1865 button.setAttribute('data-rgb', rgb);
1866 button.setAttribute('role', 'menuitem');
1867 button.setStyle('backgroundImage', 'none');
1868 listitem = Y.Node.create('<li/>');
1869 listitem.append(button);
1870 listitem.setAttribute('role', 'none');
1871 colourlist.append(listitem);
1874 body = Y.Node.create('<div/>');
1876 // Set the call back.
1877 colourlist.delegate('click', this.callback_handler, 'button', this);
1878 colourlist.delegate('key', this.callback_handler, 'down:13', 'button', this);
1880 // Set the accessible header text.
1881 this.set('headerText', M.util.get_string('colourpicker', 'assignfeedback_editpdf'));
1883 // Set the body content.
1884 body.append(colourlist);
1885 this.set('bodyContent', body);
1887 COLOURPICKER.superclass.initializer.call(this, config);
1889 callback_handler: function(e) {
1892 var callback = this.get('callback'),
1893 callbackcontext = this.get('context'),
1898 // Call the callback with the specified context.
1899 bind = Y.bind(callback, callbackcontext, e);
1904 NAME: COLOURPICKER_NAME,
1907 * The list of colours this colour picker supports.
1909 * @attribute colours
1910 * @type {String: String} (The keys of the array are the colour names and the values are localized strings)
1918 * The function called when a new colour is chosen.
1920 * @attribute callback
1929 * The context passed to the callback when a colour is chosen.
1931 * @attribute context
1940 * The prefix for the icon image names.
1942 * @attribute iconprefix
1944 * @default 'colour_'
1952 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
1953 M.assignfeedback_editpdf.colourpicker = COLOURPICKER;
1954 var STAMPPICKER_NAME = "Colourpicker",
1958 * Provides an in browser PDF editor.
1960 * @module moodle-assignfeedback_editpdf-editor
1964 * This is a drop down list of stamps.
1966 * @namespace M.assignfeedback_editpdf
1967 * @class stamppicker
1969 * @extends M.assignfeedback_editpdf.dropdown
1971 STAMPPICKER = function(config) {
1972 STAMPPICKER.superclass.constructor.apply(this, [config]);
1975 Y.extend(STAMPPICKER, M.assignfeedback_editpdf.dropdown, {
1978 * Initialise the menu.
1980 * @method initializer
1983 initializer: function(config) {
1984 var stamplist = Y.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>');
1986 // Build a list of stamped buttons.
1987 Y.each(this.get('stamps'), function(stamp) {
1988 var button, listitem, title;
1990 title = M.util.get_string('stamp', 'assignfeedback_editpdf');
1991 button = Y.Node.create('<button><img height="16" width="16" alt="' + title + '" src="' + stamp + '"/></button>');
1992 button.setAttribute('data-stamp', stamp);
1993 button.setAttribute('role', 'menuitem');
1994 button.setStyle('backgroundImage', 'none');
1995 listitem = Y.Node.create('<li/>');
1996 listitem.append(button);
1997 listitem.setAttribute('role', 'none');
1998 stamplist.append(listitem);
2002 // Set the call back.
2003 stamplist.delegate('click', this.callback_handler, 'button', this);
2004 stamplist.delegate('key', this.callback_handler, 'down:13', 'button', this);
2006 // Set the accessible header text.
2007 this.set('headerText', M.util.get_string('stamppicker', 'assignfeedback_editpdf'));
2009 // Set the body content.
2010 this.set('bodyContent', stamplist);
2012 STAMPPICKER.superclass.initializer.call(this, config);
2014 callback_handler: function(e) {
2016 var callback = this.get('callback'),
2017 callbackcontext = this.get('context'),
2022 // Call the callback with the specified context.
2023 bind = Y.bind(callback, callbackcontext, e);
2028 NAME: STAMPPICKER_NAME,
2031 * The list of stamps this stamp picker supports.
2034 * @type String[] - the stamp filenames.
2042 * The function called when a new stamp is chosen.
2044 * @attribute callback
2053 * The context passed to the callback when a stamp is chosen.
2055 * @attribute context
2065 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
2066 M.assignfeedback_editpdf.stamppicker = STAMPPICKER;
2067 var COMMENTMENUNAME = "Commentmenu",
2071 * Provides an in browser PDF editor.
2073 * @module moodle-assignfeedback_editpdf-editor
2078 * This is a drop down list of comment context functions.
2080 * @namespace M.assignfeedback_editpdf
2081 * @class commentmenu
2083 * @extends M.assignfeedback_editpdf.dropdown
2085 COMMENTMENU = function(config) {
2086 COMMENTMENU.superclass.constructor.apply(this, [config]);
2089 Y.extend(COMMENTMENU, M.assignfeedback_editpdf.dropdown, {
2092 * Initialise the menu.
2094 * @method initializer
2097 initializer: function(config) {
2103 comment = this.get('comment');
2104 // Build the list of menu items.
2105 commentlinks = Y.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>');
2107 link = Y.Node.create('<li><a tabindex="-1" href="#">' +
2108 M.util.get_string('addtoquicklist', 'assignfeedback_editpdf') +
2110 link.on('click', comment.add_to_quicklist, comment);
2111 link.on('key', comment.add_to_quicklist, 'enter,space', comment);
2113 commentlinks.append(link);
2115 link = Y.Node.create('<li><a tabindex="-1" href="#">' +
2116 M.util.get_string('deletecomment', 'assignfeedback_editpdf') +
2118 link.on('click', function(e) {
2124 link.on('key', function() {
2125 comment.menu.hide();
2127 }, 'enter,space', comment);
2129 commentlinks.append(link);
2131 link = Y.Node.create('<li><hr/></li>');
2132 commentlinks.append(link);
2134 // Set the accessible header text.
2135 this.set('headerText', M.util.get_string('commentcontextmenu', 'assignfeedback_editpdf'));
2137 body = Y.Node.create('<div/>');
2139 // Set the body content.
2140 body.append(commentlinks);
2141 this.set('bodyContent', body);
2143 COMMENTMENU.superclass.initializer.call(this, config);
2153 var commentlinks = this.get('boundingBox').one('ul');
2154 commentlinks.all('.quicklist_comment').remove(true);
2155 var comment = this.get('comment');
2157 comment.deleteme = false; // Cancel the deleting of blank comments.
2159 // Now build the list of quicklist comments.
2160 Y.each(comment.editor.quicklist.comments, function(quickcomment) {
2161 var listitem = Y.Node.create('<li class="quicklist_comment"></li>'),
2162 linkitem = Y.Node.create('<a href="#" tabindex="-1">' + quickcomment.rawtext + '</a>'),
2163 deletelinkitem = Y.Node.create('<a href="#" tabindex="-1" class="delete_quicklist_comment">' +
2164 '<img src="' + M.util.image_url('t/delete', 'core') + '" ' +
2165 'alt="' + M.util.get_string('deletecomment', 'assignfeedback_editpdf') + '"/>' +
2167 linkitem.setAttribute('title', quickcomment.rawtext);
2168 listitem.append(linkitem);
2169 listitem.append(deletelinkitem);
2171 commentlinks.append(listitem);
2173 listitem.on('click', comment.set_from_quick_comment, comment, quickcomment);
2174 listitem.on('key', comment.set_from_quick_comment, 'space,enter', comment, quickcomment);
2176 deletelinkitem.on('click', comment.remove_from_quicklist, comment, quickcomment);
2177 deletelinkitem.on('key', comment.remove_from_quicklist, 'space,enter', comment, quickcomment);
2180 COMMENTMENU.superclass.show.call(this);
2183 NAME: COMMENTMENUNAME,
2186 * The comment this menu is attached to.
2188 * @attribute comment
2189 * @type M.assignfeedback_editpdf.comment
2199 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
2200 M.assignfeedback_editpdf.commentmenu = COMMENTMENU;
2201 /* eslint-disable no-unused-vars */
2202 var COMMENTSEARCHNAME = "commentsearch",
2206 * Provides an in browser PDF editor.
2208 * @module moodle-assignfeedback_editpdf-editor
2212 * This is a searchable dialogue of comments.
2214 * @namespace M.assignfeedback_editpdf
2215 * @class commentsearch
2217 * @extends M.core.dialogue
2219 COMMENTSEARCH = function(config) {
2220 config.draggable = false;
2221 config.centered = true;
2222 config.width = '400px';
2223 config.visible = false;
2224 config.headerContent = M.util.get_string('searchcomments', 'assignfeedback_editpdf');
2225 config.footerContent = '';
2226 COMMENTSEARCH.superclass.constructor.apply(this, [config]);
2229 Y.extend(COMMENTSEARCH, M.core.dialogue, {
2231 * Initialise the menu.
2233 * @method initializer
2236 initializer: function(config) {
2244 bb = this.get('boundingBox');
2245 bb.addClass('assignfeedback_editpdf_commentsearch');
2247 editor = this.get('editor');
2248 container = Y.Node.create('<div/>');
2250 placeholder = M.util.get_string('filter', 'assignfeedback_editpdf');
2251 commentfilter = Y.Node.create('<input type="text" size="20" placeholder="' + placeholder + '"/>');
2252 container.append(commentfilter);
2253 commentlist = Y.Node.create('<ul role="menu" class="assignfeedback_editpdf_search"/>');
2254 container.append(commentlist);
2256 commentfilter.on('keyup', this.filter_search_comments, this);
2257 commentlist.delegate('click', this.focus_on_comment, 'a', this);
2258 commentlist.delegate('key', this.focus_on_comment, 'enter,space', 'a', this);
2260 // Set the body content.
2261 this.set('bodyContent', container);
2263 COMMENTSEARCH.superclass.initializer.call(this, config);
2267 * Event handler to filter the list of comments.
2270 * @method filter_search_comments
2272 filter_search_comments: function() {
2278 dialogueid = this.get('id');
2279 filternode = Y.one('#' + dialogueid + SELECTOR.SEARCHFILTER);
2280 commentslist = Y.one('#' + dialogueid + SELECTOR.SEARCHCOMMENTSLIST);
2282 filtertext = filternode.get('value');
2284 commentslist.all('li').each(function(node) {
2285 if (node.get('text').indexOf(filtertext) !== -1) {
2294 * Event handler to focus on a selected comment.
2298 * @method focus_on_comment
2300 focus_on_comment: function(e) {
2302 var target = e.target.ancestor('li'),
2303 comment = target.getData('comment'),
2304 editor = this.get('editor');
2308 comment.pageno = comment.clean().pageno;
2309 if (comment.pageno !== editor.currentpage) {
2310 // Comment is on a different page.
2311 editor.currentpage = comment.pageno;
2312 editor.change_page();
2315 comment.node = comment.drawable.nodes[0].one('textarea');
2316 comment.node.ancestor('div').removeClass('commentcollapsed');
2317 comment.node.focus();
2327 var commentlist = this.get('boundingBox').one('ul'),
2328 editor = this.get('editor');
2330 commentlist.all('li').remove(true);
2332 // Rebuild the latest list of comments.
2333 Y.each(editor.pages, function(page) {
2334 Y.each(page.comments, function(comment) {
2335 var commentnode = Y.Node.create('<li><a href="#" tabindex="-1"><pre>' + comment.rawtext + '</pre></a></li>');
2336 commentlist.append(commentnode);
2337 commentnode.setData('comment', comment);
2341 this.centerDialogue();
2342 COMMENTSEARCH.superclass.show.call(this);
2345 NAME: COMMENTSEARCHNAME,
2348 * The editor this search window is attached to.
2351 * @type M.assignfeedback_editpdf.editor
2361 Y.Base.modifyAttrs(COMMENTSEARCH, {
2363 * Whether the widget should be modal or not.
2365 * Moodle override: We override this for commentsearch to force it always true.
2372 getter: function() {
2378 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
2379 M.assignfeedback_editpdf.commentsearch = COMMENTSEARCH;
2380 // This file is part of Moodle - http://moodle.org/
2382 // Moodle is free software: you can redistribute it and/or modify
2383 // it under the terms of the GNU General Public License as published by
2384 // the Free Software Foundation, either version 3 of the License, or
2385 // (at your option) any later version.
2387 // Moodle is distributed in the hope that it will be useful,
2388 // but WITHOUT ANY WARRANTY; without even the implied warranty of
2389 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2390 // GNU General Public License for more details.
2392 // You should have received a copy of the GNU General Public License
2393 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
2396 * Provides an in browser PDF editor.
2398 * @module moodle-assignfeedback_editpdf-editor
2402 * Class representing a list of comments.
2404 * @namespace M.assignfeedback_editpdf
2406 * @param M.assignfeedback_editpdf.editor editor
2407 * @param Int gradeid
2412 * @param String colour
2413 * @param String rawtext
2415 var COMMENT = function(editor, gradeid, pageno, x, y, width, colour, rawtext) {
2418 * Reference to M.assignfeedback_editpdf.editor.
2420 * @type M.assignfeedback_editpdf.editor
2423 this.editor = editor;
2431 this.gradeid = gradeid || 0;
2439 this.x = parseInt(x, 10) || 0;
2447 this.y = parseInt(y, 10) || 0;
2455 this.width = parseInt(width, 10) || 0;
2463 this.rawtext = rawtext || '';
2466 * Comment page number
2471 this.pageno = pageno || 0;
2474 * Comment background colour.
2479 this.colour = colour || 'yellow';
2482 * Reference to M.assignfeedback_editpdf.drawable
2483 * @property drawable
2484 * @type M.assignfeedback_editpdf.drawable
2487 this.drawable = false;
2490 * Boolean used by a timeout to delete empty comments after a short delay.
2491 * @property deleteme
2495 this.deleteme = false;
2498 * Reference to the link that opens the menu.
2499 * @property menulink
2503 this.menulink = null;
2506 * Reference to the dialogue that is the context menu.
2508 * @type M.assignfeedback_editpdf.dropdown
2514 * Clean a comment record, returning an oject with only fields that are valid.
2519 this.clean = function() {
2521 gradeid: this.gradeid,
2522 x: parseInt(this.x, 10),
2523 y: parseInt(this.y, 10),
2524 width: parseInt(this.width, 10),
2525 rawtext: this.rawtext,
2526 pageno: parseInt(this.pageno, 10),
2534 * @method draw_comment
2535 * @param boolean focus - Set the keyboard focus to the new comment if true
2536 * @return M.assignfeedback_editpdf.drawable
2538 this.draw = function(focus) {
2539 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
2541 drawingcanvas = this.editor.get_dialogue_element(SELECTOR.DRAWINGCANVAS),
2549 // Lets add a contenteditable div.
2550 node = Y.Node.create('<textarea/>');
2551 container = Y.Node.create('<div class="commentdrawable"/>');
2552 label = Y.Node.create('<label/>');
2553 marker = Y.Node.create('<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.5 -0.5 13 13" ' +
2554 'preserveAspectRatio="xMinYMin meet">' +
2555 '<path d="M11 0H1C.4 0 0 .4 0 1v6c0 .6.4 1 1 1h1v4l4-4h5c.6 0 1-.4 1-1V1c0-.6-.4-1-1-1z" ' +
2556 'fill="currentColor" opacity="0.9" stroke="rgb(153, 153, 153)" stroke-width="0.5"/></svg>');
2557 menu = Y.Node.create('<a href="#"><img src="' + M.util.image_url('t/contextmenu', 'core') + '"/></a>');
2559 this.menulink = menu;
2560 container.append(label);
2562 container.append(marker);
2563 container.setAttribute('tabindex', '-1');
2564 label.setAttribute('tabindex', '0');
2565 node.setAttribute('tabindex', '-1');
2566 menu.setAttribute('tabindex', '0');
2568 if (!this.editor.get('readonly')) {
2569 container.append(menu);
2571 node.setAttribute('readonly', 'readonly');
2573 if (this.width < 100) {
2577 position = this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(this.x, this.y));
2579 width: this.width + 'px',
2580 backgroundColor: COMMENTCOLOUR[this.colour],
2581 color: COMMENTTEXTCOLOUR
2584 drawingcanvas.append(container);
2585 container.setStyle('position', 'absolute');
2586 container.setX(position.x);
2587 container.setY(position.y);
2588 drawable.store_position(container, position.x, position.y);
2589 drawable.nodes.push(container);
2590 node.set('value', this.rawtext);
2591 scrollheight = node.get('scrollHeight');
2593 'height': scrollheight + 'px',
2594 'overflow': 'hidden'
2596 marker.setStyle('color', COMMENTCOLOUR[this.colour]);
2597 this.attach_events(node, menu);
2600 } else if (editor.collapsecomments) {
2601 container.addClass('commentcollapsed');
2603 this.drawable = drawable;
2610 * Delete an empty comment if it's menu hasn't been opened in time.
2611 * @method delete_comment_later
2613 this.delete_comment_later = function() {
2614 if (this.deleteme && !this.is_menu_active()) {
2620 * Returns true if the menu is active, false otherwise.
2622 * @return bool true if menu is active, else false.
2624 this.is_menu_active = function() {
2625 return this.menu.get('visible');
2629 * Comment nodes have a bunch of event handlers attached to them directly.
2630 * This is all done here for neatness.
2633 * @method attach_comment_events
2634 * @param node - The Y.Node representing the comment.
2635 * @param menu - The Y.Node representing the menu.
2637 this.attach_events = function(node, menu) {
2638 var container = node.ancestor('div'),
2639 label = node.ancestor('label'),
2640 marker = label.next('svg');
2642 // Function to collapse a comment to a marker icon.
2643 node.collapse = function(delay) {
2644 node.collapse.delay = Y.later(delay, node, function() {
2645 if (editor.collapsecomments && !this.is_menu_active()) {
2646 container.addClass('commentcollapsed');
2651 // Function to expand a comment.
2652 node.expand = function() {
2653 if (node.getData('dragging') !== true) {
2654 if (node.collapse.delay) {
2655 node.collapse.delay.cancel();
2657 container.removeClass('commentcollapsed');
2661 // Expand comment on mouse over (under certain conditions) or click/tap.
2662 container.on('mouseenter', function() {
2663 if (editor.currentedit.tool === 'comment' || editor.currentedit.tool === 'select' || this.editor.get('readonly')) {
2667 container.on('click|tap', function() {
2672 // Functions to capture reverse tabbing events.
2673 node.on('keyup', function(e) {
2674 if (e.keyCode === 9 && e.shiftKey && menu.getAttribute('tabindex') === '0') {
2675 // User landed here via Shift+Tab (but not from this comment's menu).
2678 menu.setAttribute('tabindex', '0');
2680 menu.on('keydown', function(e) {
2681 if (e.keyCode === 9 && e.shiftKey) {
2682 // User is tabbing back to the comment node from its own menu.
2683 menu.setAttribute('tabindex', '-1');
2687 // Comment becomes "active" on label or menu focus.
2688 label.on('focus', function() {
2690 if (node.collapse.delay) {
2691 node.collapse.delay.cancel();
2693 // Give comment a tabindex to prevent focus outline being suppressed.
2694 node.setAttribute('tabindex', '0');
2695 // Expand comment and pass focus to it.
2698 // Now remove label tabindex so user can reverse tab past it.
2699 label.setAttribute('tabindex', '-1');
2701 menu.on('focus', function() {
2703 if (node.collapse.delay) {
2704 node.collapse.delay.cancel();
2706 this.deleteme = false;
2707 // Restore label tabindex so user can tab back to it from menu.
2708 label.setAttribute('tabindex', '0');
2711 // Always restore the default tabindex states when moving away.
2712 node.on('blur', function() {
2713 node.setAttribute('tabindex', '-1');
2715 label.on('blur', function() {
2716 label.setAttribute('tabindex', '0');
2719 // Collapse comment on mouse out if not currently active.
2720 container.on('mouseleave', function() {
2721 if (editor.collapsecomments && node.active !== true) {
2726 // Collapse comment on blur.
2727 container.on('blur', function() {
2728 node.active = false;
2732 if (!this.editor.get('readonly')) {
2733 // Save the text on blur.
2734 node.on('blur', function() {
2735 // Save the changes back to the comment.
2736 this.rawtext = node.get('value');
2737 this.width = parseInt(node.getStyle('width'), 10);
2740 if (this.rawtext.replace(/^\s+|\s+$/g, "") === '') {
2741 // Delete empty comments.
2742 this.deleteme = true;
2743 Y.later(400, this, this.delete_comment_later);
2745 this.editor.save_current_page();
2746 this.editor.editingcomment = false;
2749 // For delegated event handler.
2750 menu.setData('comment', this);
2752 node.on('keyup', function() {
2753 node.setStyle('height', 'auto');
2754 var scrollheight = node.get('scrollHeight'),
2755 height = parseInt(node.getStyle('height'), 10);
2757 // Webkit scrollheight fix.
2758 if (scrollheight === height + 8) {
2761 node.setStyle('height', scrollheight + 'px');
2764 node.on('gesturemovestart', function(e) {
2765 if (editor.currentedit.tool === 'select') {
2767 if (editor.collapsecomments) {
2768 node.setData('offsetx', 8);
2769 node.setData('offsety', 8);
2771 node.setData('offsetx', e.clientX - container.getX());
2772 node.setData('offsety', e.clientY - container.getY());
2776 node.on('gesturemove', function(e) {
2777 if (editor.currentedit.tool === 'select') {
2778 var x = e.clientX - node.getData('offsetx'),
2779 y = e.clientY - node.getData('offsety'),
2784 if (node.getData('dragging') !== true) {
2785 // Collapse comment during move.
2787 node.setData('dragging', true);
2790 newlocation = this.editor.get_canvas_coordinates(new M.assignfeedback_editpdf.point(x, y));
2791 bounds = this.editor.get_canvas_bounds(true);
2796 bounds.height -= 24;
2797 // Clip to the window size - the comment icon size.
2798 newlocation.clip(bounds);
2800 this.x = newlocation.x;
2801 this.y = newlocation.y;
2803 windowlocation = this.editor.get_window_coordinates(newlocation);
2804 container.setX(windowlocation.x);
2805 container.setY(windowlocation.y);
2806 this.drawable.store_position(container, windowlocation.x, windowlocation.y);
2809 node.on('gesturemoveend', function() {
2810 if (editor.currentedit.tool === 'select') {
2811 if (node.getData('dragging') === true) {
2812 node.setData('dragging', false);
2814 this.editor.save_current_page();
2817 marker.on('gesturemovestart', function(e) {
2818 if (editor.currentedit.tool === 'select') {
2820 node.setData('offsetx', e.clientX - container.getX());
2821 node.setData('offsety', e.clientY - container.getY());
2825 marker.on('gesturemove', function(e) {
2826 if (editor.currentedit.tool === 'select') {
2827 var x = e.clientX - node.getData('offsetx'),
2828 y = e.clientY - node.getData('offsety'),
2833 if (node.getData('dragging') !== true) {
2834 // Collapse comment during move.
2836 node.setData('dragging', true);
2839 newlocation = this.editor.get_canvas_coordinates(new M.assignfeedback_editpdf.point(x, y));
2840 bounds = this.editor.get_canvas_bounds(true);
2845 bounds.height -= 24;
2846 // Clip to the window size - the comment icon size.
2847 newlocation.clip(bounds);
2849 this.x = newlocation.x;
2850 this.y = newlocation.y;
2852 windowlocation = this.editor.get_window_coordinates(newlocation);
2853 container.setX(windowlocation.x);
2854 container.setY(windowlocation.y);
2855 this.drawable.store_position(container, windowlocation.x, windowlocation.y);
2858 marker.on('gesturemoveend', function() {
2859 if (editor.currentedit.tool === 'select') {
2860 if (node.getData('dragging') === true) {
2861 node.setData('dragging', false);
2863 this.editor.save_current_page();
2867 this.menu = new M.assignfeedback_editpdf.commentmenu({
2868 buttonNode: this.menulink,
2878 this.remove = function() {
2882 comments = this.editor.pages[this.editor.currentpage].comments;
2883 for (i = 0; i < comments.length; i++) {
2884 if (comments[i] === this) {
2885 comments.splice(i, 1);
2886 this.drawable.erase();
2887 this.editor.save_current_page();
2894 * Event handler to remove a comment from the users quicklist.
2897 * @method remove_from_quicklist
2899 this.remove_from_quicklist = function(e, quickcomment) {
2901 e.stopPropagation();
2905 this.editor.quicklist.remove(quickcomment);
2909 * A quick comment was selected in the list, update the active comment and redraw the page.
2913 * @method set_from_quick_comment
2915 this.set_from_quick_comment = function(e, quickcomment) {
2919 this.deleteme = false;
2921 this.rawtext = quickcomment.rawtext;
2922 this.width = quickcomment.width;
2923 this.colour = quickcomment.colour;
2925 this.editor.save_current_page();
2927 this.editor.redraw();
2929 this.node = this.drawable.nodes[0].one('textarea');
2930 this.node.ancestor('div').removeClass('commentcollapsed');
2935 * Event handler to add a comment to the users quicklist.
2938 * @method add_to_quicklist
2940 this.add_to_quicklist = function(e) {
2943 this.editor.quicklist.add(this);
2947 * Draw the in progress edit.
2950 * @method draw_current_edit
2951 * @param M.assignfeedback_editpdf.edit edit
2953 this.draw_current_edit = function(edit) {
2954 var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
2958 bounds = new M.assignfeedback_editpdf.rect();
2959 bounds.bound([edit.start, edit.end]);
2961 // We will draw a box with the current background colour.
2962 shape = this.editor.graphic.addShape({
2964 width: bounds.width,
2965 height: bounds.height,
2967 color: COMMENTCOLOUR[edit.commentcolour]
2973 drawable.shapes.push(shape);
2979 * Promote the current edit to a real comment.
2982 * @method init_from_edit
2983 * @param M.assignfeedback_editpdf.edit edit
2984 * @return bool true if comment bound is more than min width/height, else false.
2986 this.init_from_edit = function(edit) {
2987 var bounds = new M.assignfeedback_editpdf.rect();
2988 bounds.bound([edit.start, edit.end]);
2990 // Minimum comment width.
2991 if (bounds.width < 100) {
2995 // Save the current edit to the server and the current page list.
2997 this.gradeid = this.editor.get('gradeid');
2998 this.pageno = this.editor.currentpage;
3001 this.width = bounds.width;
3002 this.colour = edit.commentcolour;
3005 return (bounds.has_min_width() && bounds.has_min_height());
3009 * Update comment position when rotating page.
3011 * @method updatePosition
3013 this.updatePosition = function() {
3014 var node = this.drawable.nodes[0].one('textarea');
3015 var container = node.ancestor('div');
3017 var newlocation = new M.assignfeedback_editpdf.point(this.x, this.y);
3018 var windowlocation = this.editor.get_window_coordinates(newlocation);
3020 container.setX(windowlocation.x);
3021 container.setY(windowlocation.y);
3022 this.drawable.store_position(container, windowlocation.x, windowlocation.y);
3027 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
3028 M.assignfeedback_editpdf.comment = COMMENT;
3029 // This file is part of Moodle - http://moodle.org/
3031 // Moodle is free software: you can redistribute it and/or modify
3032 // it under the terms of the GNU General Public License as published by
3033 // the Free Software Foundation, either version 3 of the License, or
3034 // (at your option) any later version.
3036 // Moodle is distributed in the hope that it will be useful,
3037 // but WITHOUT ANY WARRANTY; without even the implied warranty of
3038 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3039 // GNU General Public License for more details.
3041 // You should have received a copy of the GNU General Public License
3042 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
3045 * Provides an in browser PDF editor.
3047 * @module moodle-assignfeedback_editpdf-editor
3051 * Class representing a users quick comment.
3053 * @namespace M.assignfeedback_editpdf
3054 * @class quickcomment
3056 var QUICKCOMMENT = function(id, rawtext, width, colour) {
3059 * Quick comment text.
3064 this.rawtext = rawtext || '';
3075 * Width of the comment
3080 this.width = width || 100;
3083 * Colour of the comment.
3088 this.colour = colour || "yellow";
3091 M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
3092 M.assignfeedback_editpdf.quickcomment = QUICKCOMMENT;
3093 // This file is part of Moodle - http://moodle.org/
3095 // Moodle is free software: you can redistribute it and/or modify
3096 // it under the terms of the GNU General Public License as published by
3097 // the Free Software Foundation, either version 3 of the License, or
3098 // (at your option) any later version.
3100 // Moodle is distributed in the hope that it will be useful,
3101 // but WITHOUT ANY WARRANTY; without even the implied warranty of