first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
File diff suppressed because one or more lines are too long
@@ -0,0 +1,30 @@
{
"name": "moodle-assignfeedback_editpdf-editor",
"builds": {
"moodle-assignfeedback_editpdf-editor": {
"jsfiles": [
"globals.js",
"point.js",
"rect.js",
"edit.js",
"drawable.js",
"annotation.js",
"annotationline.js",
"annotationrectangle.js",
"annotationoval.js",
"annotationpen.js",
"annotationhighlight.js",
"annotationstamp.js",
"dropdown.js",
"colourpicker.js",
"stamppicker.js",
"commentmenu.js",
"commentsearch.js",
"comment.js",
"quickcomment.js",
"quickcommentlist.js",
"editor.js"
]
}
}
}
@@ -0,0 +1,339 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* Class representing a highlight.
*
* @namespace M.assignfeedback_editpdf
* @class annotation
* @constructor
*/
var ANNOTATION = function(config) {
ANNOTATION.superclass.constructor.apply(this, [config]);
};
ANNOTATION.NAME = "annotation";
ANNOTATION.ATTRS = {};
Y.extend(ANNOTATION, Y.Base, {
/**
* Reference to M.assignfeedback_editpdf.editor.
* @property editor
* @type M.assignfeedback_editpdf.editor
* @public
*/
editor: null,
/**
* Grade id
* @property gradeid
* @type Int
* @public
*/
gradeid: 0,
/**
* Comment page number
* @property pageno
* @type Int
* @public
*/
pageno: 0,
/**
* X position
* @property x
* @type Int
* @public
*/
x: 0,
/**
* Y position
* @property y
* @type Int
* @public
*/
y: 0,
/**
* Ending x position
* @property endx
* @type Int
* @public
*/
endx: 0,
/**
* Ending y position
* @property endy
* @type Int
* @public
*/
endy: 0,
/**
* Path
* @property path
* @type String - list of points like x1,y1:x2,y2
* @public
*/
path: '',
/**
* Tool.
* @property type
* @type String
* @public
*/
type: 'rect',
/**
* Annotation colour.
* @property colour
* @type String
* @public
*/
colour: 'red',
/**
* Reference to M.assignfeedback_editpdf.drawable
* @property drawable
* @type M.assignfeedback_editpdf.drawable
* @public
*/
drawable: false,
/**
* Initialise the annotation.
*
* @method initializer
* @return void
*/
initializer: function(config) {
this.editor = config.editor || null;
this.gradeid = parseInt(config.gradeid, 10) || 0;
this.pageno = parseInt(config.pageno, 10) || 0;
this.x = parseInt(config.x, 10) || 0;
this.y = parseInt(config.y, 10) || 0;
this.endx = parseInt(config.endx, 10) || 0;
this.endy = parseInt(config.endy, 10) || 0;
this.path = config.path || '';
this.type = config.type || 'rect';
this.colour = config.colour || 'red';
this.drawable = false;
},
/**
* Clean a comment record, returning an oject with only fields that are valid.
* @public
* @method clean
* @return {}
*/
clean: function() {
return {
gradeid: this.gradeid,
x: parseInt(this.x, 10),
y: parseInt(this.y, 10),
endx: parseInt(this.endx, 10),
endy: parseInt(this.endy, 10),
type: this.type,
path: this.path,
pageno: this.pageno,
colour: this.colour
};
},
/**
* Draw a selection around this annotation if it is selected.
* @public
* @method draw_highlight
* @return M.assignfeedback_editpdf.drawable
*/
draw_highlight: function() {
var bounds,
drawingregion = this.editor.get_dialogue_element(SELECTOR.DRAWINGREGION),
offsetcanvas = this.editor.get_dialogue_element(SELECTOR.DRAWINGCANVAS).getXY(),
shape;
if (this.editor.currentannotation === this) {
// Draw a highlight around the annotation.
bounds = new M.assignfeedback_editpdf.rect();
bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y),
new M.assignfeedback_editpdf.point(this.endx, this.endy)]);
shape = this.editor.graphic.addShape({
type: Y.Rect,
width: bounds.width,
height: bounds.height,
stroke: {
weight: STROKEWEIGHT,
color: SELECTEDBORDERCOLOUR
},
fill: {
color: SELECTEDFILLCOLOUR
},
x: bounds.x,
y: bounds.y
});
this.drawable.shapes.push(shape);
// Add a delete X to the annotation.
var deleteicon = Y.Node.create('<img src="' + M.util.image_url('trash', 'assignfeedback_editpdf') + '"/>'),
deletelink = Y.Node.create('<a href="#" role="button"></a>');
deleteicon.setAttrs({
'alt': M.util.get_string('deleteannotation', 'assignfeedback_editpdf')
});
deleteicon.setStyles({
'backgroundColor': 'white'
});
deletelink.addClass('deleteannotationbutton');
deletelink.append(deleteicon);
drawingregion.append(deletelink);
deletelink.setData('annotation', this);
deletelink.on('click', this.remove, this);
deletelink.on('key', this.remove, 'space,enter', this);
deletelink.setX(offsetcanvas[0] + bounds.x + bounds.width - 18);
deletelink.setY(offsetcanvas[1] + bounds.y + 6);
this.drawable.nodes.push(deletelink);
}
return this.drawable;
},
/**
* Draw an annotation
* @public
* @method draw
* @return M.assignfeedback_editpdf.drawable|false
*/
draw: function() {
// Should be overridden by the subclass.
this.draw_highlight();
return this.drawable;
},
/**
* Delete an annotation
* @protected
* @method remove
* @param event
*/
remove: function(e) {
var annotations,
i;
e.preventDefault();
annotations = this.editor.pages[this.editor.currentpage].annotations;
for (i = 0; i < annotations.length; i++) {
if (annotations[i] === this) {
annotations.splice(i, 1);
if (this.drawable) {
this.drawable.erase();
}
this.editor.currentannotation = false;
this.editor.save_current_page();
return;
}
}
},
/**
* Move an annotation to a new location.
* @public
* @param int newx
* @param int newy
* @method move_annotation
*/
move: function(newx, newy) {
var diffx = newx - this.x,
diffy = newy - this.y,
newpath, oldpath, xy,
x, y;
this.x += diffx;
this.y += diffy;
this.endx += diffx;
this.endy += diffy;
if (this.path) {
newpath = [];
oldpath = this.path.split(':');
Y.each(oldpath, function(position) {
xy = position.split(',');
x = parseInt(xy[0], 10);
y = parseInt(xy[1], 10);
newpath.push((x + diffx) + ',' + (y + diffy));
});
this.path = newpath.join(':');
}
if (this.drawable) {
this.drawable.erase();
}
this.editor.drawables.push(this.draw());
},
/**
* Draw the in progress edit.
*
* @public
* @method draw_current_edit
* @param M.assignfeedback_editpdf.edit edit
*/
draw_current_edit: function(edit) {
var noop = edit && false;
// Override me please.
return noop;
},
/**
* Promote the current edit to a real annotation.
*
* @public
* @method init_from_edit
* @param M.assignfeedback_editpdf.edit edit
* @return bool if width/height is more than min. required.
*/
init_from_edit: function(edit) {
var bounds = new M.assignfeedback_editpdf.rect();
bounds.bound([edit.start, edit.end]);
this.gradeid = this.editor.get('gradeid');
this.pageno = this.editor.currentpage;
this.x = bounds.x;
this.y = bounds.y;
this.endx = bounds.x + bounds.width;
this.endy = bounds.y + bounds.height;
this.colour = edit.annotationcolour;
this.path = '';
return (bounds.has_min_width() && bounds.has_min_height());
}
});
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.annotation = ANNOTATION;
@@ -0,0 +1,153 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* Class representing a highlight.
*
* @namespace M.assignfeedback_editpdf
* @class annotationhighlight
* @extends M.assignfeedback_editpdf.annotation
* @module moodle-assignfeedback_editpdf-editor
*/
var ANNOTATIONHIGHLIGHT = function(config) {
ANNOTATIONHIGHLIGHT.superclass.constructor.apply(this, [config]);
};
ANNOTATIONHIGHLIGHT.NAME = "annotationhighlight";
ANNOTATIONHIGHLIGHT.ATTRS = {};
Y.extend(ANNOTATIONHIGHLIGHT, M.assignfeedback_editpdf.annotation, {
/**
* Draw a highlight annotation
* @protected
* @method draw
* @return M.assignfeedback_editpdf.drawable
*/
draw: function() {
var drawable,
shape,
bounds,
highlightcolour;
drawable = new M.assignfeedback_editpdf.drawable(this.editor);
bounds = new M.assignfeedback_editpdf.rect();
bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y),
new M.assignfeedback_editpdf.point(this.endx, this.endy)]);
highlightcolour = ANNOTATIONCOLOUR[this.colour];
// Add an alpha channel to the rgb colour.
highlightcolour = highlightcolour.replace('rgb', 'rgba');
highlightcolour = highlightcolour.replace(')', ',0.5)');
shape = this.editor.graphic.addShape({
type: Y.Rect,
width: bounds.width,
height: bounds.height,
stroke: false,
fill: {
color: highlightcolour
},
x: bounds.x,
y: bounds.y
});
drawable.shapes.push(shape);
this.drawable = drawable;
return ANNOTATIONHIGHLIGHT.superclass.draw.apply(this);
},
/**
* Draw the in progress edit.
*
* @public
* @method draw_current_edit
* @param M.assignfeedback_editpdf.edit edit
*/
draw_current_edit: function(edit) {
var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
shape,
bounds,
highlightcolour;
bounds = new M.assignfeedback_editpdf.rect();
bounds.bound([new M.assignfeedback_editpdf.point(edit.start.x, edit.start.y),
new M.assignfeedback_editpdf.point(edit.end.x, edit.end.y)]);
// Set min. width of highlight.
if (!bounds.has_min_width()) {
bounds.set_min_width();
}
highlightcolour = ANNOTATIONCOLOUR[edit.annotationcolour];
// Add an alpha channel to the rgb colour.
highlightcolour = highlightcolour.replace('rgb', 'rgba');
highlightcolour = highlightcolour.replace(')', ',0.5)');
// We will draw a box with the current background colour.
shape = this.editor.graphic.addShape({
type: Y.Rect,
width: bounds.width,
height: 20,
stroke: false,
fill: {
color: highlightcolour
},
x: bounds.x,
y: edit.start.y - 10
});
drawable.shapes.push(shape);
return drawable;
},
/**
* Promote the current edit to a real annotation.
*
* @public
* @method init_from_edit
* @param M.assignfeedback_editpdf.edit edit
* @return bool true if highlight bound is more than min width/height, else false.
*/
init_from_edit: function(edit) {
var bounds = new M.assignfeedback_editpdf.rect();
bounds.bound([edit.start, edit.end]);
this.gradeid = this.editor.get('gradeid');
this.pageno = this.editor.currentpage;
this.x = bounds.x;
this.y = edit.start.y - 10;
this.endx = bounds.x + bounds.width;
this.endy = edit.start.y + 10;
this.colour = edit.annotationcolour;
this.page = '';
return (bounds.has_min_width());
}
});
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.annotationhighlight = ANNOTATIONHIGHLIGHT;
@@ -0,0 +1,120 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* Class representing a line.
*
* @namespace M.assignfeedback_editpdf
* @class annotationline
* @extends M.assignfeedback_editpdf.annotation
*/
var ANNOTATIONLINE = function(config) {
ANNOTATIONLINE.superclass.constructor.apply(this, [config]);
};
ANNOTATIONLINE.NAME = "annotationline";
ANNOTATIONLINE.ATTRS = {};
Y.extend(ANNOTATIONLINE, M.assignfeedback_editpdf.annotation, {
/**
* Draw a line annotation
* @protected
* @method draw
* @return M.assignfeedback_editpdf.drawable
*/
draw: function() {
var drawable,
shape;
drawable = new M.assignfeedback_editpdf.drawable(this.editor);
shape = this.editor.graphic.addShape({
type: Y.Path,
fill: false,
stroke: {
weight: STROKEWEIGHT,
color: ANNOTATIONCOLOUR[this.colour]
}
});
shape.moveTo(this.x, this.y);
shape.lineTo(this.endx, this.endy);
shape.end();
drawable.shapes.push(shape);
this.drawable = drawable;
return ANNOTATIONLINE.superclass.draw.apply(this);
},
/**
* Draw the in progress edit.
*
* @public
* @method draw_current_edit
* @param M.assignfeedback_editpdf.edit edit
*/
draw_current_edit: function(edit) {
var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
shape;
shape = this.editor.graphic.addShape({
type: Y.Path,
fill: false,
stroke: {
weight: STROKEWEIGHT,
color: ANNOTATIONCOLOUR[edit.annotationcolour]
}
});
shape.moveTo(edit.start.x, edit.start.y);
shape.lineTo(edit.end.x, edit.end.y);
shape.end();
drawable.shapes.push(shape);
return drawable;
},
/**
* Promote the current edit to a real annotation.
*
* @public
* @method init_from_edit
* @param M.assignfeedback_editpdf.edit edit
* @return bool true if line bound is more than min width/height, else false.
*/
init_from_edit: function(edit) {
this.gradeid = this.editor.get('gradeid');
this.pageno = this.editor.currentpage;
this.x = edit.start.x;
this.y = edit.start.y;
this.endx = edit.end.x;
this.endy = edit.end.y;
this.colour = edit.annotationcolour;
this.path = '';
return !(((this.endx - this.x) === 0) && ((this.endy - this.y) === 0));
}
});
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.annotationline = ANNOTATIONLINE;
@@ -0,0 +1,114 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* Class representing a oval.
*
* @namespace M.assignfeedback_editpdf
* @class annotationoval
* @extends M.assignfeedback_editpdf.annotation
*/
var ANNOTATIONOVAL = function(config) {
ANNOTATIONOVAL.superclass.constructor.apply(this, [config]);
};
ANNOTATIONOVAL.NAME = "annotationoval";
ANNOTATIONOVAL.ATTRS = {};
Y.extend(ANNOTATIONOVAL, M.assignfeedback_editpdf.annotation, {
/**
* Draw a oval annotation
* @protected
* @method draw
* @return M.assignfeedback_editpdf.drawable
*/
draw: function() {
var drawable,
bounds,
shape;
drawable = new M.assignfeedback_editpdf.drawable(this.editor);
bounds = new M.assignfeedback_editpdf.rect();
bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y),
new M.assignfeedback_editpdf.point(this.endx, this.endy)]);
shape = this.editor.graphic.addShape({
type: Y.Ellipse,
width: bounds.width,
height: bounds.height,
stroke: {
weight: STROKEWEIGHT,
color: ANNOTATIONCOLOUR[this.colour]
},
x: bounds.x,
y: bounds.y
});
drawable.shapes.push(shape);
this.drawable = drawable;
return ANNOTATIONOVAL.superclass.draw.apply(this);
},
/**
* Draw the in progress edit.
*
* @public
* @method draw_current_edit
* @param M.assignfeedback_editpdf.edit edit
*/
draw_current_edit: function(edit) {
var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
shape,
bounds;
bounds = new M.assignfeedback_editpdf.rect();
bounds.bound([new M.assignfeedback_editpdf.point(edit.start.x, edit.start.y),
new M.assignfeedback_editpdf.point(edit.end.x, edit.end.y)]);
// Set min. width and height of oval.
if (!bounds.has_min_width()) {
bounds.set_min_width();
}
if (!bounds.has_min_height()) {
bounds.set_min_height();
}
shape = this.editor.graphic.addShape({
type: Y.Ellipse,
width: bounds.width,
height: bounds.height,
stroke: {
weight: STROKEWEIGHT,
color: ANNOTATIONCOLOUR[edit.annotationcolour]
},
x: bounds.x,
y: bounds.y
});
drawable.shapes.push(shape);
return drawable;
}
});
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.annotationoval = ANNOTATIONOVAL;
@@ -0,0 +1,160 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* Class representing a pen.
*
* @namespace M.assignfeedback_editpdf
* @class annotationpen
* @extends M.assignfeedback_editpdf.annotation
*/
var ANNOTATIONPEN = function(config) {
ANNOTATIONPEN.superclass.constructor.apply(this, [config]);
};
ANNOTATIONPEN.NAME = "annotationpen";
ANNOTATIONPEN.ATTRS = {};
Y.extend(ANNOTATIONPEN, M.assignfeedback_editpdf.annotation, {
/**
* Draw a pen annotation
* @protected
* @method draw
* @return M.assignfeedback_editpdf.drawable
*/
draw: function() {
var drawable,
shape,
first,
positions,
xy;
drawable = new M.assignfeedback_editpdf.drawable(this.editor);
shape = this.editor.graphic.addShape({
type: Y.Path,
fill: false,
stroke: {
weight: STROKEWEIGHT,
color: ANNOTATIONCOLOUR[this.colour]
}
});
first = true;
// Recreate the pen path array.
positions = this.path.split(':');
// Redraw all the lines.
Y.each(positions, function(position) {
xy = position.split(',');
if (first) {
shape.moveTo(xy[0], xy[1]);
first = false;
} else {
shape.lineTo(xy[0], xy[1]);
}
}, this);
shape.end();
drawable.shapes.push(shape);
this.drawable = drawable;
return ANNOTATIONPEN.superclass.draw.apply(this);
},
/**
* Draw the in progress edit.
*
* @public
* @method draw_current_edit
* @param M.assignfeedback_editpdf.edit edit
*/
draw_current_edit: function(edit) {
var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
shape,
first;
shape = this.editor.graphic.addShape({
type: Y.Path,
fill: false,
stroke: {
weight: STROKEWEIGHT,
color: ANNOTATIONCOLOUR[edit.annotationcolour]
}
});
first = true;
// Recreate the pen path array.
// Redraw all the lines.
Y.each(edit.path, function(position) {
if (first) {
shape.moveTo(position.x, position.y);
first = false;
} else {
shape.lineTo(position.x, position.y);
}
}, this);
shape.end();
drawable.shapes.push(shape);
return drawable;
},
/**
* Promote the current edit to a real annotation.
*
* @public
* @method init_from_edit
* @param M.assignfeedback_editpdf.edit edit
* @return bool true if pen bound is more than min width/height, else false.
*/
init_from_edit: function(edit) {
var bounds = new M.assignfeedback_editpdf.rect(),
pathlist = [],
i = 0;
// This will get the boundaries of all points in the path.
bounds.bound(edit.path);
for (i = 0; i < edit.path.length; i++) {
pathlist.push(parseInt(edit.path[i].x, 10) + ',' + parseInt(edit.path[i].y, 10));
}
this.gradeid = this.editor.get('gradeid');
this.pageno = this.editor.currentpage;
this.x = bounds.x;
this.y = bounds.y;
this.endx = bounds.x + bounds.width;
this.endy = bounds.y + bounds.height;
this.colour = edit.annotationcolour;
this.path = pathlist.join(':');
return (bounds.has_min_width() || bounds.has_min_height());
}
});
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.annotationpen = ANNOTATIONPEN;
@@ -0,0 +1,114 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* Class representing a rectangle.
*
* @namespace M.assignfeedback_editpdf
* @class annotationrectangle
* @extends M.assignfeedback_editpdf.annotation
*/
var ANNOTATIONRECTANGLE = function(config) {
ANNOTATIONRECTANGLE.superclass.constructor.apply(this, [config]);
};
ANNOTATIONRECTANGLE.NAME = "annotationrectangle";
ANNOTATIONRECTANGLE.ATTRS = {};
Y.extend(ANNOTATIONRECTANGLE, M.assignfeedback_editpdf.annotation, {
/**
* Draw a rectangle annotation
* @protected
* @method draw
* @return M.assignfeedback_editpdf.drawable
*/
draw: function() {
var drawable,
bounds,
shape;
drawable = new M.assignfeedback_editpdf.drawable(this.editor);
bounds = new M.assignfeedback_editpdf.rect();
bounds.bound([new M.assignfeedback_editpdf.point(this.x, this.y),
new M.assignfeedback_editpdf.point(this.endx, this.endy)]);
shape = this.editor.graphic.addShape({
type: Y.Rect,
width: bounds.width,
height: bounds.height,
stroke: {
weight: STROKEWEIGHT,
color: ANNOTATIONCOLOUR[this.colour]
},
x: bounds.x,
y: bounds.y
});
drawable.shapes.push(shape);
this.drawable = drawable;
return ANNOTATIONRECTANGLE.superclass.draw.apply(this);
},
/**
* Draw the in progress edit.
*
* @public
* @method draw_current_edit
* @param M.assignfeedback_editpdf.edit edit
*/
draw_current_edit: function(edit) {
var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
shape,
bounds;
bounds = new M.assignfeedback_editpdf.rect();
bounds.bound([new M.assignfeedback_editpdf.point(edit.start.x, edit.start.y),
new M.assignfeedback_editpdf.point(edit.end.x, edit.end.y)]);
// Set min. width and height of rectangle.
if (!bounds.has_min_width()) {
bounds.set_min_width();
}
if (!bounds.has_min_height()) {
bounds.set_min_height();
}
shape = this.editor.graphic.addShape({
type: Y.Rect,
width: bounds.width,
height: bounds.height,
stroke: {
weight: STROKEWEIGHT,
color: ANNOTATIONCOLOUR[edit.annotationcolour]
},
x: bounds.x,
y: bounds.y
});
drawable.shapes.push(shape);
return drawable;
}
});
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.annotationrectangle = ANNOTATIONRECTANGLE;
@@ -0,0 +1,176 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* Class representing a stamp.
*
* @namespace M.assignfeedback_editpdf
* @class annotationstamp
* @extends M.assignfeedback_editpdf.annotation
*/
var ANNOTATIONSTAMP = function(config) {
ANNOTATIONSTAMP.superclass.constructor.apply(this, [config]);
};
ANNOTATIONSTAMP.NAME = "annotationstamp";
ANNOTATIONSTAMP.ATTRS = {};
Y.extend(ANNOTATIONSTAMP, M.assignfeedback_editpdf.annotation, {
/**
* Draw a stamp annotation
* @protected
* @method draw
* @return M.assignfeedback_editpdf.drawable
*/
draw: function() {
var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
drawingcanvas = this.editor.get_dialogue_element(SELECTOR.DRAWINGCANVAS),
node,
position;
position = this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(this.x, this.y));
node = Y.Node.create('<div/>');
node.addClass('annotation');
node.addClass('stamp');
node.setStyles({
'position': 'absolute',
'display': 'inline-block',
'backgroundImage': 'url(' + this.editor.get_stamp_image_url(this.path) + ')',
'width': (this.endx - this.x),
'height': (this.endy - this.y),
'backgroundSize': '100% 100%'
});
drawingcanvas.append(node);
node.setX(position.x);
node.setY(position.y);
drawable.store_position(node, position.x, position.y);
// Bind events only when editing.
if (!this.editor.get('readonly')) {
// Pass through the event handlers on the div.
node.on('gesturemovestart', this.editor.edit_start, null, this.editor);
node.on('gesturemove', this.editor.edit_move, null, this.editor);
node.on('gesturemoveend', this.editor.edit_end, null, this.editor);
}
drawable.nodes.push(node);
this.drawable = drawable;
return ANNOTATIONSTAMP.superclass.draw.apply(this);
},
/**
* Draw the in progress edit.
*
* @public
* @method draw_current_edit
* @param M.assignfeedback_editpdf.edit edit
*/
draw_current_edit: function(edit) {
var bounds = new M.assignfeedback_editpdf.rect(),
drawable = new M.assignfeedback_editpdf.drawable(this.editor),
drawingregion = this.editor.get_dialogue_element(SELECTOR.DRAWINGREGION),
node,
position;
bounds.bound([edit.start, edit.end]);
position = this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(bounds.x, bounds.y));
node = Y.Node.create('<div/>');
node.addClass('annotation');
node.addClass('stamp');
node.setStyles({
'position': 'absolute',
'display': 'inline-block',
'backgroundImage': 'url(' + this.editor.get_stamp_image_url(edit.stamp) + ')',
'width': bounds.width,
'height': bounds.height,
'backgroundSize': '100% 100%'
});
drawingregion.append(node);
node.setX(position.x);
node.setY(position.y);
drawable.store_position(node, position.x, position.y);
drawable.nodes.push(node);
return drawable;
},
/**
* Promote the current edit to a real annotation.
*
* @public
* @method init_from_edit
* @param M.assignfeedback_editpdf.edit edit
* @return bool if width/height is more than min. required.
*/
init_from_edit: function(edit) {
var bounds = new M.assignfeedback_editpdf.rect();
bounds.bound([edit.start, edit.end]);
if (bounds.width < 40) {
bounds.width = 40;
}
if (bounds.height < 40) {
bounds.height = 40;
}
this.gradeid = this.editor.get('gradeid');
this.pageno = this.editor.currentpage;
this.x = bounds.x;
this.y = bounds.y;
this.endx = bounds.x + bounds.width;
this.endy = bounds.y + bounds.height;
this.colour = edit.annotationcolour;
this.path = edit.stamp;
// Min width and height is always more than 40px.
return true;
},
/**
* Move an annotation to a new location.
* @public
* @param int newx
* @param int newy
* @method move_annotation
*/
move: function(newx, newy) {
var diffx = newx - this.x,
diffy = newy - this.y;
this.x += diffx;
this.y += diffy;
this.endx += diffx;
this.endy += diffy;
if (this.drawable) {
this.drawable.erase();
}
this.editor.drawables.push(this.draw());
}
});
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.annotationstamp = ANNOTATIONSTAMP;
@@ -0,0 +1,132 @@
var COLOURPICKER_NAME = "Colourpicker",
COLOURPICKER;
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* COLOURPICKER
* This is a drop down list of colours.
*
* @namespace M.assignfeedback_editpdf
* @class colourpicker
* @constructor
* @extends M.assignfeedback_editpdf.dropdown
*/
COLOURPICKER = function(config) {
COLOURPICKER.superclass.constructor.apply(this, [config]);
};
Y.extend(COLOURPICKER, M.assignfeedback_editpdf.dropdown, {
/**
* Initialise the menu.
*
* @method initializer
* @return void
*/
initializer: function(config) {
var colourlist = Y.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>'),
body;
// Build a list of coloured buttons.
Y.each(this.get('colours'), function(rgb, colour) {
var button, listitem, title, img, iconname;
title = M.util.get_string(colour, 'assignfeedback_editpdf');
iconname = this.get('iconprefix') + colour;
img = M.util.image_url(iconname, 'assignfeedback_editpdf');
button = Y.Node.create('<button><img alt="' + title + '" src="' + img + '"/></button>');
button.setAttribute('data-colour', colour);
button.setAttribute('data-rgb', rgb);
button.setAttribute('role', 'menuitem');
button.setStyle('backgroundImage', 'none');
listitem = Y.Node.create('<li/>');
listitem.append(button);
listitem.setAttribute('role', 'none');
colourlist.append(listitem);
}, this);
body = Y.Node.create('<div/>');
// Set the call back.
colourlist.delegate('click', this.callback_handler, 'button', this);
colourlist.delegate('key', this.callback_handler, 'down:13', 'button', this);
// Set the accessible header text.
this.set('headerText', M.util.get_string('colourpicker', 'assignfeedback_editpdf'));
// Set the body content.
body.append(colourlist);
this.set('bodyContent', body);
COLOURPICKER.superclass.initializer.call(this, config);
},
callback_handler: function(e) {
e.preventDefault();
var callback = this.get('callback'),
callbackcontext = this.get('context'),
bind;
this.hide();
// Call the callback with the specified context.
bind = Y.bind(callback, callbackcontext, e);
bind();
}
}, {
NAME: COLOURPICKER_NAME,
ATTRS: {
/**
* The list of colours this colour picker supports.
*
* @attribute colours
* @type {String: String} (The keys of the array are the colour names and the values are localized strings)
* @default {}
*/
colours: {
value: {}
},
/**
* The function called when a new colour is chosen.
*
* @attribute callback
* @type function
* @default null
*/
callback: {
value: null
},
/**
* The context passed to the callback when a colour is chosen.
*
* @attribute context
* @type Y.Node
* @default null
*/
context: {
value: null
},
/**
* The prefix for the icon image names.
*
* @attribute iconprefix
* @type String
* @default 'colour_'
*/
iconprefix: {
value: 'colour_'
}
}
});
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.colourpicker = COLOURPICKER;
+649
View File
@@ -0,0 +1,649 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* Class representing a list of comments.
*
* @namespace M.assignfeedback_editpdf
* @class comment
* @param M.assignfeedback_editpdf.editor editor
* @param Int gradeid
* @param Int pageno
* @param Int x
* @param Int y
* @param Int width
* @param String colour
* @param String rawtext
*/
var COMMENT = function(editor, gradeid, pageno, x, y, width, colour, rawtext) {
/**
* Reference to M.assignfeedback_editpdf.editor.
* @property editor
* @type M.assignfeedback_editpdf.editor
* @public
*/
this.editor = editor;
/**
* Grade id
* @property gradeid
* @type Int
* @public
*/
this.gradeid = gradeid || 0;
/**
* X position
* @property x
* @type Int
* @public
*/
this.x = parseInt(x, 10) || 0;
/**
* Y position
* @property y
* @type Int
* @public
*/
this.y = parseInt(y, 10) || 0;
/**
* Comment width
* @property width
* @type Int
* @public
*/
this.width = parseInt(width, 10) || 0;
/**
* Comment rawtext
* @property rawtext
* @type String
* @public
*/
this.rawtext = rawtext || '';
/**
* Comment page number
* @property pageno
* @type Int
* @public
*/
this.pageno = pageno || 0;
/**
* Comment background colour.
* @property colour
* @type String
* @public
*/
this.colour = colour || 'yellow';
/**
* Reference to M.assignfeedback_editpdf.drawable
* @property drawable
* @type M.assignfeedback_editpdf.drawable
* @public
*/
this.drawable = false;
/**
* Boolean used by a timeout to delete empty comments after a short delay.
* @property deleteme
* @type Boolean
* @public
*/
this.deleteme = false;
/**
* Reference to the link that opens the menu.
* @property menulink
* @type Y.Node
* @public
*/
this.menulink = null;
/**
* Reference to the dialogue that is the context menu.
* @property menu
* @type M.assignfeedback_editpdf.dropdown
* @public
*/
this.menu = null;
/**
* Clean a comment record, returning an oject with only fields that are valid.
* @public
* @method clean
* @return {}
*/
this.clean = function() {
return {
gradeid: this.gradeid,
x: parseInt(this.x, 10),
y: parseInt(this.y, 10),
width: parseInt(this.width, 10),
rawtext: this.rawtext,
pageno: parseInt(this.pageno, 10),
colour: this.colour
};
};
/**
* Draw a comment.
* @public
* @method draw_comment
* @param boolean focus - Set the keyboard focus to the new comment if true
* @return M.assignfeedback_editpdf.drawable
*/
this.draw = function(focus) {
var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
node,
drawingcanvas = this.editor.get_dialogue_element(SELECTOR.DRAWINGCANVAS),
container,
label,
marker,
menu,
position,
scrollheight;
// Lets add a contenteditable div.
node = Y.Node.create('<textarea/>');
container = Y.Node.create('<div class="commentdrawable"/>');
label = Y.Node.create('<label/>');
marker = Y.Node.create('<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.5 -0.5 13 13" ' +
'preserveAspectRatio="xMinYMin meet">' +
'<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" ' +
'fill="currentColor" opacity="0.9" stroke="rgb(153, 153, 153)" stroke-width="0.5"/></svg>');
menu = Y.Node.create('<a href="#"><img src="' + M.util.image_url('t/contextmenu', 'core') + '"/></a>');
this.menulink = menu;
container.append(label);
label.append(node);
container.append(marker);
container.setAttribute('tabindex', '-1');
label.setAttribute('tabindex', '0');
node.setAttribute('tabindex', '-1');
menu.setAttribute('tabindex', '0');
if (!this.editor.get('readonly')) {
container.append(menu);
} else {
node.setAttribute('readonly', 'readonly');
}
if (this.width < 100) {
this.width = 100;
}
position = this.editor.get_window_coordinates(new M.assignfeedback_editpdf.point(this.x, this.y));
node.setStyles({
width: this.width + 'px',
backgroundColor: COMMENTCOLOUR[this.colour],
color: COMMENTTEXTCOLOUR
});
drawingcanvas.append(container);
container.setStyle('position', 'absolute');
container.setX(position.x);
container.setY(position.y);
drawable.store_position(container, position.x, position.y);
drawable.nodes.push(container);
node.set('value', this.rawtext);
scrollheight = node.get('scrollHeight');
node.setStyles({
'height': scrollheight + 'px',
'overflow': 'hidden'
});
marker.setStyle('color', COMMENTCOLOUR[this.colour]);
this.attach_events(node, menu);
if (focus) {
node.focus();
} else if (editor.collapsecomments) {
container.addClass('commentcollapsed');
}
this.drawable = drawable;
return drawable;
};
/**
* Delete an empty comment if it's menu hasn't been opened in time.
* @method delete_comment_later
*/
this.delete_comment_later = function() {
if (this.deleteme && !this.is_menu_active()) {
this.remove();
}
};
/**
* Returns true if the menu is active, false otherwise.
*
* @return bool true if menu is active, else false.
*/
this.is_menu_active = function() {
return this.menu !== null && this.menu.get('visible');
};
/**
* Comment nodes have a bunch of event handlers attached to them directly.
* This is all done here for neatness.
*
* @protected
* @method attach_comment_events
* @param node - The Y.Node representing the comment.
* @param menu - The Y.Node representing the menu.
*/
this.attach_events = function(node, menu) {
var container = node.ancestor('div'),
label = node.ancestor('label'),
marker = label.next('svg');
// Function to collapse a comment to a marker icon.
node.collapse = function(delay) {
node.collapse.delay = Y.later(delay, node, function() {
if (editor.collapsecomments && !this.is_menu_active()) {
container.addClass('commentcollapsed');
}
}.bind(this));
}.bind(this);
// Function to expand a comment.
node.expand = function() {
if (node.getData('dragging') !== true) {
if (node.collapse.delay) {
node.collapse.delay.cancel();
}
container.removeClass('commentcollapsed');
}
};
// Expand comment on mouse over (under certain conditions) or click/tap.
container.on('mouseenter', function() {
if (editor.currentedit.tool === 'comment' || editor.currentedit.tool === 'select' || this.editor.get('readonly')) {
node.expand();
}
}, this);
container.on('click|tap', function() {
node.expand();
node.focus();
}, this);
// Functions to capture reverse tabbing events.
node.on('keyup', function(e) {
if (e.keyCode === 9 && e.shiftKey && menu.getAttribute('tabindex') === '0') {
// User landed here via Shift+Tab (but not from this comment's menu).
menu.focus();
}
menu.setAttribute('tabindex', '0');
}, this);
menu.on('keydown', function(e) {
if (e.keyCode === 9 && e.shiftKey) {
// User is tabbing back to the comment node from its own menu.
menu.setAttribute('tabindex', '-1');
}
}, this);
// Comment becomes "active" on label or menu focus.
label.on('focus', function() {
node.active = true;
if (node.collapse.delay) {
node.collapse.delay.cancel();
}
// Give comment a tabindex to prevent focus outline being suppressed.
node.setAttribute('tabindex', '0');
// Expand comment and pass focus to it.
node.expand();
node.focus();
// Now remove label tabindex so user can reverse tab past it.
label.setAttribute('tabindex', '-1');
}, this);
menu.on('focus', function() {
node.active = true;
if (node.collapse.delay) {
node.collapse.delay.cancel();
}
this.deleteme = false;
// Restore label tabindex so user can tab back to it from menu.
label.setAttribute('tabindex', '0');
}, this);
// Always restore the default tabindex states when moving away.
node.on('blur', function() {
node.setAttribute('tabindex', '-1');
}, this);
label.on('blur', function() {
label.setAttribute('tabindex', '0');
}, this);
// Collapse comment on mouse out if not currently active.
container.on('mouseleave', function() {
if (editor.collapsecomments && node.active !== true) {
node.collapse(400);
}
}, this);
// Collapse comment on blur.
container.on('blur', function() {
node.active = false;
node.collapse(800);
}, this);
if (!this.editor.get('readonly')) {
// Save the text on blur.
node.on('blur', function() {
// Save the changes back to the comment.
this.rawtext = node.get('value');
this.width = parseInt(node.getStyle('width'), 10);
// Trim.
if (this.rawtext.replace(/^\s+|\s+$/g, "") === '') {
// Delete empty comments.
this.deleteme = true;
Y.later(400, this, this.delete_comment_later);
}
this.editor.save_current_page();
this.editor.editingcomment = false;
}, this);
// For delegated event handler.
menu.setData('comment', this);
node.on('keyup', function() {
node.setStyle('height', 'auto');
var scrollheight = node.get('scrollHeight'),
height = parseInt(node.getStyle('height'), 10);
// Webkit scrollheight fix.
if (scrollheight === height + 8) {
scrollheight -= 8;
}
node.setStyle('height', scrollheight + 'px');
});
node.on('gesturemovestart', function(e) {
if (editor.currentedit.tool === 'select') {
e.preventDefault();
if (editor.collapsecomments) {
node.setData('offsetx', 8);
node.setData('offsety', 8);
} else {
node.setData('offsetx', e.clientX - container.getX());
node.setData('offsety', e.clientY - container.getY());
}
}
});
node.on('gesturemove', function(e) {
if (editor.currentedit.tool === 'select') {
var x = e.clientX - node.getData('offsetx'),
y = e.clientY - node.getData('offsety'),
newlocation,
windowlocation,
bounds;
if (node.getData('dragging') !== true) {
// Collapse comment during move.
node.collapse(0);
node.setData('dragging', true);
}
newlocation = this.editor.get_canvas_coordinates(new M.assignfeedback_editpdf.point(x, y));
bounds = this.editor.get_canvas_bounds(true);
bounds.x = 0;
bounds.y = 0;
bounds.width -= 24;
bounds.height -= 24;
// Clip to the window size - the comment icon size.
newlocation.clip(bounds);
this.x = newlocation.x;
this.y = newlocation.y;
windowlocation = this.editor.get_window_coordinates(newlocation);
container.setX(windowlocation.x);
container.setY(windowlocation.y);
this.drawable.store_position(container, windowlocation.x, windowlocation.y);
}
}, null, this);
node.on('gesturemoveend', function() {
if (editor.currentedit.tool === 'select') {
if (node.getData('dragging') === true) {
node.setData('dragging', false);
}
this.editor.save_current_page();
}
}, null, this);
marker.on('gesturemovestart', function(e) {
if (editor.currentedit.tool === 'select') {
e.preventDefault();
node.setData('offsetx', e.clientX - container.getX());
node.setData('offsety', e.clientY - container.getY());
node.expand();
}
});
marker.on('gesturemove', function(e) {
if (editor.currentedit.tool === 'select') {
var x = e.clientX - node.getData('offsetx'),
y = e.clientY - node.getData('offsety'),
newlocation,
windowlocation,
bounds;
if (node.getData('dragging') !== true) {
// Collapse comment during move.
node.collapse(100);
node.setData('dragging', true);
}
newlocation = this.editor.get_canvas_coordinates(new M.assignfeedback_editpdf.point(x, y));
bounds = this.editor.get_canvas_bounds(true);
bounds.x = 0;
bounds.y = 0;
bounds.width -= 24;
bounds.height -= 24;
// Clip to the window size - the comment icon size.
newlocation.clip(bounds);
this.x = newlocation.x;
this.y = newlocation.y;
windowlocation = this.editor.get_window_coordinates(newlocation);
container.setX(windowlocation.x);
container.setY(windowlocation.y);
this.drawable.store_position(container, windowlocation.x, windowlocation.y);
}
}, null, this);
marker.on('gesturemoveend', function() {
if (editor.currentedit.tool === 'select') {
if (node.getData('dragging') === true) {
node.setData('dragging', false);
}
this.editor.save_current_page();
}
}, null, this);
this.menu = new M.assignfeedback_editpdf.commentmenu({
buttonNode: this.menulink,
comment: this
});
}
};
/**
* Delete a comment.
* @method remove
*/
this.remove = function() {
var i = 0;
var comments;
comments = this.editor.pages[this.editor.currentpage].comments;
for (i = 0; i < comments.length; i++) {
if (comments[i] === this) {
comments.splice(i, 1);
this.drawable.erase();
this.editor.save_current_page();
return;
}
}
};
/**
* Event handler to remove a comment from the users quicklist.
*
* @protected
* @method remove_from_quicklist
*/
this.remove_from_quicklist = function(e, quickcomment) {
e.preventDefault();
e.stopPropagation();
this.menu.hide();
this.editor.quicklist.remove(quickcomment);
};
/**
* A quick comment was selected in the list, update the active comment and redraw the page.
*
* @param Event e
* @protected
* @method set_from_quick_comment
*/
this.set_from_quick_comment = function(e, quickcomment) {
e.preventDefault();
this.menu.hide();
this.deleteme = false;
this.rawtext = quickcomment.rawtext;
this.width = quickcomment.width;
this.colour = quickcomment.colour;
this.editor.save_current_page();
this.editor.redraw();
this.node = this.drawable.nodes[0].one('textarea');
this.node.ancestor('div').removeClass('commentcollapsed');
this.node.focus();
};
/**
* Event handler to add a comment to the users quicklist.
*
* @protected
* @method add_to_quicklist
*/
this.add_to_quicklist = function(e) {
e.preventDefault();
this.menu.hide();
this.editor.quicklist.add(this);
};
/**
* Draw the in progress edit.
*
* @public
* @method draw_current_edit
* @param M.assignfeedback_editpdf.edit edit
*/
this.draw_current_edit = function(edit) {
var drawable = new M.assignfeedback_editpdf.drawable(this.editor),
shape,
bounds;
bounds = new M.assignfeedback_editpdf.rect();
bounds.bound([edit.start, edit.end]);
// We will draw a box with the current background colour.
shape = this.editor.graphic.addShape({
type: Y.Rect,
width: bounds.width,
height: bounds.height,
fill: {
color: COMMENTCOLOUR[edit.commentcolour]
},
x: bounds.x,
y: bounds.y
});
drawable.shapes.push(shape);
return drawable;
};
/**
* Promote the current edit to a real comment.
*
* @public
* @method init_from_edit
* @param M.assignfeedback_editpdf.edit edit
* @return bool true if comment bound is more than min width/height, else false.
*/
this.init_from_edit = function(edit) {
var bounds = new M.assignfeedback_editpdf.rect();
bounds.bound([edit.start, edit.end]);
// Minimum comment width.
if (bounds.width < 100) {
bounds.width = 100;
}
// Save the current edit to the server and the current page list.
this.gradeid = this.editor.get('gradeid');
this.pageno = this.editor.currentpage;
this.x = bounds.x;
this.y = bounds.y;
this.width = bounds.width;
this.colour = edit.commentcolour;
this.rawtext = '';
return (bounds.has_min_width() && bounds.has_min_height());
};
/**
* Update comment position when rotating page.
* @public
* @method updatePosition
*/
this.updatePosition = function() {
var node = this.drawable.nodes[0].one('textarea');
var container = node.ancestor('div');
var newlocation = new M.assignfeedback_editpdf.point(this.x, this.y);
var windowlocation = this.editor.get_window_coordinates(newlocation);
container.setX(windowlocation.x);
container.setY(windowlocation.y);
this.drawable.store_position(container, windowlocation.x, windowlocation.y);
};
};
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.comment = COMMENT;
@@ -0,0 +1,134 @@
var COMMENTMENUNAME = "Commentmenu",
COMMENTMENU;
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* COMMENTMENU
* This is a drop down list of comment context functions.
*
* @namespace M.assignfeedback_editpdf
* @class commentmenu
* @constructor
* @extends M.assignfeedback_editpdf.dropdown
*/
COMMENTMENU = function(config) {
COMMENTMENU.superclass.constructor.apply(this, [config]);
};
Y.extend(COMMENTMENU, M.assignfeedback_editpdf.dropdown, {
/**
* Initialise the menu.
*
* @method initializer
* @return void
*/
initializer: function(config) {
var commentlinks,
link,
body,
comment;
comment = this.get('comment');
// Build the list of menu items.
commentlinks = Y.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>');
link = Y.Node.create('<li><a tabindex="-1" href="#">' +
M.util.get_string('addtoquicklist', 'assignfeedback_editpdf') +
'</a></li>');
link.on('click', comment.add_to_quicklist, comment);
link.on('key', comment.add_to_quicklist, 'enter,space', comment);
commentlinks.append(link);
link = Y.Node.create('<li><a tabindex="-1" href="#">' +
M.util.get_string('deletecomment', 'assignfeedback_editpdf') +
'</a></li>');
link.on('click', function(e) {
e.preventDefault();
this.menu.hide();
this.remove();
}, comment);
link.on('key', function() {
comment.menu.hide();
comment.remove();
}, 'enter,space', comment);
commentlinks.append(link);
link = Y.Node.create('<li><hr/></li>');
commentlinks.append(link);
// Set the accessible header text.
this.set('headerText', M.util.get_string('commentcontextmenu', 'assignfeedback_editpdf'));
body = Y.Node.create('<div/>');
// Set the body content.
body.append(commentlinks);
this.set('bodyContent', body);
COMMENTMENU.superclass.initializer.call(this, config);
},
/**
* Show the menu.
*
* @method show
* @return void
*/
show: function() {
var commentlinks = this.get('boundingBox').one('ul');
commentlinks.all('.quicklist_comment').remove(true);
var comment = this.get('comment');
comment.deleteme = false; // Cancel the deleting of blank comments.
// Now build the list of quicklist comments.
Y.each(comment.editor.quicklist.comments, function(quickcomment) {
var listitem = Y.Node.create('<li class="quicklist_comment"></li>'),
linkitem = Y.Node.create('<a href="#" tabindex="-1">' + quickcomment.rawtext + '</a>'),
deletelinkitem = Y.Node.create('<a href="#" tabindex="-1" class="delete_quicklist_comment">' +
'<img src="' + M.util.image_url('t/delete', 'core') + '" ' +
'alt="' + M.util.get_string('deletecomment', 'assignfeedback_editpdf') + '"/>' +
'</a>');
linkitem.setAttribute('title', quickcomment.rawtext);
listitem.append(linkitem);
listitem.append(deletelinkitem);
commentlinks.append(listitem);
listitem.on('click', comment.set_from_quick_comment, comment, quickcomment);
listitem.on('key', comment.set_from_quick_comment, 'space,enter', comment, quickcomment);
deletelinkitem.on('click', comment.remove_from_quicklist, comment, quickcomment);
deletelinkitem.on('key', comment.remove_from_quicklist, 'space,enter', comment, quickcomment);
}, this);
COMMENTMENU.superclass.show.call(this);
}
}, {
NAME: COMMENTMENUNAME,
ATTRS: {
/**
* The comment this menu is attached to.
*
* @attribute comment
* @type M.assignfeedback_editpdf.comment
* @default null
*/
comment: {
value: null
}
}
});
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.commentmenu = COMMENTMENU;
@@ -0,0 +1,177 @@
/* eslint-disable no-unused-vars */
var COMMENTSEARCHNAME = "commentsearch",
COMMENTSEARCH;
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* This is a searchable dialogue of comments.
*
* @namespace M.assignfeedback_editpdf
* @class commentsearch
* @constructor
* @extends M.core.dialogue
*/
COMMENTSEARCH = function(config) {
config.draggable = false;
config.centered = true;
config.width = '400px';
config.visible = false;
config.headerContent = M.util.get_string('searchcomments', 'assignfeedback_editpdf');
config.footerContent = '';
COMMENTSEARCH.superclass.constructor.apply(this, [config]);
};
Y.extend(COMMENTSEARCH, M.core.dialogue, {
/**
* Initialise the menu.
*
* @method initializer
* @return void
*/
initializer: function() {
var editor,
container,
placeholder,
commentfilter,
commentlist,
bb;
bb = this.get('boundingBox');
bb.addClass('assignfeedback_editpdf_commentsearch');
editor = this.get('editor');
container = Y.Node.create('<div/>');
placeholder = M.util.get_string('filter', 'assignfeedback_editpdf');
commentfilter = Y.Node.create('<input type="text" size="20" placeholder="' + placeholder + '"/>');
container.append(commentfilter);
commentlist = Y.Node.create('<ul role="menu" class="assignfeedback_editpdf_search"/>');
container.append(commentlist);
commentfilter.on('keyup', this.filter_search_comments, this);
commentlist.delegate('click', this.focus_on_comment, 'a', this);
commentlist.delegate('key', this.focus_on_comment, 'enter,space', 'a', this);
// Set the body content.
this.set('bodyContent', container);
},
/**
* Event handler to filter the list of comments.
*
* @protected
* @method filter_search_comments
*/
filter_search_comments: function() {
var filternode,
commentslist,
filtertext,
dialogueid;
dialogueid = this.get('id');
filternode = Y.one('#' + dialogueid + SELECTOR.SEARCHFILTER);
commentslist = Y.one('#' + dialogueid + SELECTOR.SEARCHCOMMENTSLIST);
filtertext = filternode.get('value');
commentslist.all('li').each(function(node) {
if (node.get('text').indexOf(filtertext) !== -1) {
node.show();
} else {
node.hide();
}
});
},
/**
* Event handler to focus on a selected comment.
*
* @param Event e
* @protected
* @method focus_on_comment
*/
focus_on_comment: function(e) {
e.preventDefault();
var target = e.target.ancestor('li'),
comment = target.getData('comment'),
editor = this.get('editor');
this.hide();
comment.pageno = comment.clean().pageno;
if (comment.pageno !== editor.currentpage) {
// Comment is on a different page.
editor.currentpage = comment.pageno;
editor.change_page();
}
comment.node = comment.drawable.nodes[0].one('textarea');
comment.node.ancestor('div').removeClass('commentcollapsed');
comment.node.focus();
},
/**
* Show the menu.
*
* @method show
* @return void
*/
show: function() {
var commentlist = this.get('boundingBox').one('ul'),
editor = this.get('editor');
commentlist.all('li').remove(true);
// Rebuild the latest list of comments.
Y.each(editor.pages, function(page) {
Y.each(page.comments, function(comment) {
var commentnode = Y.Node.create('<li><a href="#" tabindex="-1"><pre>' + comment.rawtext + '</pre></a></li>');
commentlist.append(commentnode);
commentnode.setData('comment', comment);
}, this);
}, this);
this.centerDialogue();
COMMENTSEARCH.superclass.show.call(this);
}
}, {
NAME: COMMENTSEARCHNAME,
ATTRS: {
/**
* The editor this search window is attached to.
*
* @attribute editor
* @type M.assignfeedback_editpdf.editor
* @default null
*/
editor: {
value: null
}
}
});
Y.Base.modifyAttrs(COMMENTSEARCH, {
/**
* Whether the widget should be modal or not.
*
* Moodle override: We override this for commentsearch to force it always true.
*
* @attribute Modal
* @type Boolean
* @default true
*/
modal: {
getter: function() {
return true;
}
}
});
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.commentsearch = COMMENTSEARCH;
@@ -0,0 +1,112 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* Class representing a drawable thing which contains both Y.Nodes, and Y.Shapes.
*
* @namespace M.assignfeedback_editpdf
* @param M.assignfeedback_editpdf.editor editor
* @class drawable
*/
var DRAWABLE = function(editor) {
/**
* Reference to M.assignfeedback_editpdf.editor.
* @property editor
* @type M.assignfeedback_editpdf.editor
* @public
*/
this.editor = editor;
/**
* Array of Y.Shape
* @property shapes
* @type Y.Shape[]
* @public
*/
this.shapes = [];
/**
* Array of Y.Node
* @property nodes
* @type Y.Node[]
* @public
*/
this.nodes = [];
/**
* Delete the shapes from the drawable.
* @protected
* @method erase_drawable
*/
this.erase = function() {
if (this.shapes) {
while (this.shapes.length > 0) {
this.editor.graphic.removeShape(this.shapes.pop());
}
}
if (this.nodes) {
while (this.nodes.length > 0) {
this.nodes.pop().remove();
}
}
};
/**
* Update the positions of all absolutely positioned nodes, when the drawing canvas is scrolled
* @public
* @method scroll_update
* @param scrollx int
* @param scrolly int
*/
this.scroll_update = function(scrollx, scrolly) {
var i, x, y;
for (i = 0; i < this.nodes.length; i++) {
x = this.nodes[i].getData('x');
y = this.nodes[i].getData('y');
if (x !== undefined && y !== undefined) {
this.nodes[i].setX(parseInt(x, 10) - scrollx);
this.nodes[i].setY(parseInt(y, 10) - scrolly);
}
}
};
/**
* Store the initial position of the node, so it can be updated when the drawing canvas is scrolled
* @public
* @method store_position
* @param container
* @param x
* @param y
*/
this.store_position = function(container, x, y) {
var drawingregion, scrollx, scrolly;
drawingregion = this.editor.get_dialogue_element(SELECTOR.DRAWINGREGION);
scrollx = parseInt(drawingregion.get('scrollLeft'), 10);
scrolly = parseInt(drawingregion.get('scrollTop'), 10);
container.setData('x', x + scrollx);
container.setData('y', y + scrolly);
};
};
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.drawable = DRAWABLE;
@@ -0,0 +1,126 @@
var DROPDOWN_NAME = "Dropdown menu",
DROPDOWN;
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* This is a drop down list of buttons triggered (and aligned to) a button.
*
* @namespace M.assignfeedback_editpdf
* @class dropdown
* @constructor
* @extends M.core.dialogue
*/
DROPDOWN = function(config) {
config.draggable = false;
config.centered = false;
config.width = 'auto';
config.visible = false;
config.footerContent = '';
DROPDOWN.superclass.constructor.apply(this, [config]);
};
Y.extend(DROPDOWN, M.core.dialogue, {
/**
* Initialise the menu.
*
* @method initializer
* @return void
*/
initializer: function(config) {
var button, body, headertext, bb;
DROPDOWN.superclass.initializer.call(this, config);
bb = this.get('boundingBox');
bb.addClass('assignfeedback_editpdf_dropdown');
// Align the menu to the button that opens it.
button = this.get('buttonNode');
// Close the menu when clicked outside (excluding the button that opened the menu).
body = this.bodyNode;
headertext = Y.Node.create('<h3/>');
headertext.addClass('accesshide');
headertext.setHTML(this.get('headerText'));
body.prepend(headertext);
body.on('clickoutside', function(e) {
if (this.get('visible')) {
// Note: we need to compare ids because for some reason - sometimes button is an Object, not a Y.Node.
if (e.target.get('id') !== button.get('id') && e.target.ancestor().get('id') !== button.get('id')) {
e.preventDefault();
this.hide();
}
}
}, this);
button.on('click', function(e) {
e.preventDefault(); this.show();
}, this);
button.on('key', this.show, 'enter,space', this);
},
/**
* Override the show method to align to the button.
*
* @method show
* @return void
*/
show: function() {
var button = this.get('buttonNode'),
result = DROPDOWN.superclass.show.call(this);
this.align(button, [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]);
return result;
}
}, {
NAME: DROPDOWN_NAME,
ATTRS: {
/**
* The header for the drop down (only accessible to screen readers).
*
* @attribute headerText
* @type String
* @default ''
*/
headerText: {
value: ''
},
/**
* The button used to show/hide this drop down menu.
*
* @attribute buttonNode
* @type Y.Node
* @default null
*/
buttonNode: {
value: null
}
}
});
Y.Base.modifyAttrs(DROPDOWN, {
/**
* Whether the widget should be modal or not.
*
* Moodle override: We override this for commentsearch to force it always false.
*
* @attribute Modal
* @type Boolean
* @default false
*/
modal: {
getter: function() {
return false;
}
}
});
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.dropdown = DROPDOWN;
+104
View File
@@ -0,0 +1,104 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* EDIT
*
* @namespace M.assignfeedback_editpdf
* @class edit
*/
var EDIT = function() {
/**
* Starting point for the edit.
* @property start
* @type M.assignfeedback_editpdf.point|false
* @public
*/
this.start = false;
/**
* Finishing point for the edit.
* @property end
* @type M.assignfeedback_editpdf.point|false
* @public
*/
this.end = false;
/**
* Starting time for the edit.
* @property starttime
* @type int
* @public
*/
this.starttime = 0;
/**
* Starting point for the currently selected annotation.
* @property annotationstart
* @type M.assignfeedback_editpdf.point|false
* @public
*/
this.annotationstart = false;
/**
* The currently selected tool
* @property tool
* @type String
* @public
*/
this.tool = "drag";
/**
* The currently comment colour
* @property commentcolour
* @type String
* @public
*/
this.commentcolour = 'yellow';
/**
* The currently annotation colour
* @property annotationcolour
* @type String
* @public
*/
this.annotationcolour = 'red';
/**
* The current stamp image.
* @property stamp
* @type String
* @public
*/
this.stamp = '';
/**
* List of points the the current drawing path.
* @property path
* @type M.assignfeedback_editpdf.point[]
* @public
*/
this.path = [];
};
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.edit = EDIT;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,85 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/* eslint-disable no-unused-vars */
/**
* A list of globals used by this module.
*
* @module moodle-assignfeedback_editpdf-editor
*/
var AJAXBASE = M.cfg.wwwroot + '/mod/assign/feedback/editpdf/ajax.php',
AJAXBASEPROGRESS = M.cfg.wwwroot + '/mod/assign/feedback/editpdf/ajax_progress.php',
CSS = {
DIALOGUE: 'assignfeedback_editpdf_widget'
},
SELECTOR = {
PREVIOUSBUTTON: '.navigate-previous-button',
NEXTBUTTON: ' .navigate-next-button',
SEARCHCOMMENTSBUTTON: '.searchcommentsbutton',
EXPCOLCOMMENTSBUTTON: '.expcolcommentsbutton',
SEARCHFILTER: '.assignfeedback_editpdf_commentsearch input',
SEARCHCOMMENTSLIST: '.assignfeedback_editpdf_commentsearch ul',
PAGESELECT: '.navigate-page-select',
LOADINGICON: '.loading',
PROGRESSBARCONTAINER: '.progress-info.progress-striped',
DRAWINGREGION: '.drawingregion',
DRAWINGCANVAS: '.drawingcanvas',
SAVE: '.savebutton',
COMMENTCOLOURBUTTON: '.commentcolourbutton',
COMMENTMENU: '.commentdrawable a',
ANNOTATIONCOLOURBUTTON: '.annotationcolourbutton',
DELETEANNOTATIONBUTTON: '.deleteannotationbutton',
WARNINGMESSAGECONTAINER: '.warningmessages',
ICONMESSAGECONTAINER: '.infoicon',
UNSAVEDCHANGESDIV: '.assignfeedback_editpdf_warningmessages',
UNSAVEDCHANGESINPUT: 'input[name="assignfeedback_editpdf_haschanges"]',
STAMPSBUTTON: '.currentstampbutton',
USERINFOREGION: '[data-region="user-info"]',
ROTATELEFTBUTTON: '.rotateleftbutton',
ROTATERIGHTBUTTON: '.rotaterightbutton',
DIALOGUE: '.' + CSS.DIALOGUE
},
SELECTEDBORDERCOLOUR = 'rgba(200, 200, 255, 0.9)',
SELECTEDFILLCOLOUR = 'rgba(200, 200, 255, 0.5)',
COMMENTTEXTCOLOUR = 'rgb(51, 51, 51)',
COMMENTCOLOUR = {
'white': 'rgb(255,255,255)',
'yellow': 'rgb(255,236,174)',
'red': 'rgb(249,181,179)',
'green': 'rgb(214,234,178)',
'blue': 'rgb(203,217,237)',
'clear': 'rgba(255,255,255, 0)'
},
ANNOTATIONCOLOUR = {
'white': 'rgb(255,255,255)',
'yellow': 'rgb(255,207,53)',
'red': 'rgb(239,69,64)',
'green': 'rgb(152,202,62)',
'blue': 'rgb(125,159,211)',
'black': 'rgb(51,51,51)'
},
CLICKTIMEOUT = 300,
TOOLSELECTOR = {
'comment': '.commentbutton',
'pen': '.penbutton',
'line': '.linebutton',
'rectangle': '.rectanglebutton',
'oval': '.ovalbutton',
'stamp': '.stampbutton',
'select': '.selectbutton',
'drag': '.dragbutton',
'highlight': '.highlightbutton'
},
STROKEWEIGHT = 4;
+73
View File
@@ -0,0 +1,73 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* Class representing a 2d point.
*
* @namespace M.assignfeedback_editpdf
* @param Number x
* @param Number y
* @class point
*/
var POINT = function(x, y) {
/**
* X coordinate.
* @property x
* @type int
* @public
*/
this.x = parseInt(x, 10);
/**
* Y coordinate.
* @property y
* @type int
* @public
*/
this.y = parseInt(y, 10);
/**
* Clip this point to the rect
* @method clip
* @param M.assignfeedback_editpdf.point
* @public
*/
this.clip = function(bounds) {
if (this.x < bounds.x) {
this.x = bounds.x;
}
if (this.x > (bounds.x + bounds.width)) {
this.x = bounds.x + bounds.width;
}
if (this.y < bounds.y) {
this.y = bounds.y;
}
if (this.y > (bounds.y + bounds.height)) {
this.y = bounds.y + bounds.height;
}
// For chaining.
return this;
};
};
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.point = POINT;
@@ -0,0 +1,64 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* Class representing a users quick comment.
*
* @namespace M.assignfeedback_editpdf
* @class quickcomment
*/
var QUICKCOMMENT = function(id, rawtext, width, colour) {
/**
* Quick comment text.
* @property rawtext
* @type String
* @public
*/
this.rawtext = rawtext || '';
/**
* ID of the comment
* @property id
* @type Int
* @public
*/
this.id = id || 0;
/**
* Width of the comment
* @property width
* @type Int
* @public
*/
this.width = width || 100;
/**
* Colour of the comment.
* @property colour
* @type String
* @public
*/
this.colour = colour || "yellow";
};
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.quickcomment = QUICKCOMMENT;
@@ -0,0 +1,207 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* Class representing a users list of quick comments.
*
* @namespace M.assignfeedback_editpdf
* @class quickcommentlist
*/
var QUICKCOMMENTLIST = function(editor) {
/**
* Reference to M.assignfeedback_editpdf.editor.
* @property editor
* @type M.assignfeedback_editpdf.editor
* @public
*/
this.editor = editor;
/**
* Array of Comments
* @property shapes
* @type M.assignfeedback_editpdf.quickcomment[]
* @public
*/
this.comments = [];
/**
* Add a comment to the users quicklist.
*
* @protected
* @method add
*/
this.add = function(comment) {
var ajaxurl = AJAXBASE,
config;
// Do not save empty comments.
if (comment.rawtext === '') {
return;
}
config = {
method: 'post',
context: this,
sync: false,
data: {
'sesskey': M.cfg.sesskey,
'action': 'addtoquicklist',
'userid': this.editor.get('userid'),
'commenttext': comment.rawtext,
'width': comment.width,
'colour': comment.colour,
'attemptnumber': this.editor.get('attemptnumber'),
'assignmentid': this.editor.get('assignmentid')
},
on: {
success: function(tid, response) {
var jsondata, quickcomment;
try {
jsondata = Y.JSON.parse(response.responseText);
if (jsondata.error) {
return new M.core.ajaxException(jsondata);
} else {
quickcomment = new M.assignfeedback_editpdf.quickcomment(jsondata.id,
jsondata.rawtext,
jsondata.width,
jsondata.colour);
this.comments.push(quickcomment);
this.comments.sort(function(a, b) {
return a.rawtext.localeCompare(b.rawtext);
});
}
} catch (e) {
return new M.core.exception(e);
}
},
failure: function(tid, response) {
return M.core.exception(response.responseText);
}
}
};
Y.io(ajaxurl, config);
};
/**
* Remove a comment from the users quicklist.
*
* @public
* @method remove
*/
this.remove = function(comment) {
var ajaxurl = AJAXBASE,
config;
// Should not happen.
if (!comment) {
return;
}
config = {
method: 'post',
context: this,
sync: false,
data: {
'sesskey': M.cfg.sesskey,
'action': 'removefromquicklist',
'userid': this.editor.get('userid'),
'commentid': comment.id,
'attemptnumber': this.editor.get('attemptnumber'),
'assignmentid': this.editor.get('assignmentid')
},
on: {
success: function() {
var i;
// Find and remove the comment from the quicklist.
i = this.comments.indexOf(comment);
if (i >= 0) {
this.comments.splice(i, 1);
}
},
failure: function(tid, response) {
return M.core.exception(response.responseText);
}
}
};
Y.io(ajaxurl, config);
};
/**
* Load the users quick comments list.
*
* @protected
* @method load_quicklist
*/
this.load = function() {
var ajaxurl = AJAXBASE,
config;
config = {
method: 'get',
context: this,
sync: false,
data: {
'sesskey': M.cfg.sesskey,
'action': 'loadquicklist',
'userid': this.editor.get('userid'),
'attemptnumber': this.editor.get('attemptnumber'),
'assignmentid': this.editor.get('assignmentid')
},
on: {
success: function(tid, response) {
var jsondata;
try {
jsondata = Y.JSON.parse(response.responseText);
if (jsondata.error) {
return new M.core.ajaxException(jsondata);
} else {
Y.each(jsondata, function(comment) {
var quickcomment = new M.assignfeedback_editpdf.quickcomment(comment.id,
comment.rawtext,
comment.width,
comment.colour);
this.comments.push(quickcomment);
}, this);
this.comments.sort(function(a, b) {
return a.rawtext.localeCompare(b.rawtext);
});
}
} catch (e) {
return new M.core.exception(e);
}
},
failure: function(tid, response) {
return M.core.exception(response.responseText);
}
}
};
Y.io(ajaxurl, config);
};
};
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.quickcommentlist = QUICKCOMMENTLIST;
+143
View File
@@ -0,0 +1,143 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* Class representing a 2d rect.
*
* @namespace M.assignfeedback_editpdf
* @param int x
* @param int y
* @param int width
* @param int height
* @class rect
*/
var RECT = function(x, y, width, height) {
/**
* X coordinate.
* @property x
* @type int
* @public
*/
this.x = x;
/**
* Y coordinate.
* @property y
* @type int
* @public
*/
this.y = y;
/**
* Width
* @property width
* @type int
* @public
*/
this.width = width;
/**
* Height
* @property height
* @type int
* @public
*/
this.height = height;
/**
* Set this rect to represent the smallest possible rectangle containing this list of points.
* @method bounds
* @param M.assignfeedback_editpdf.point[]
* @public
*/
this.bound = function(points) {
var minx = 0,
maxx = 0,
miny = 0,
maxy = 0,
i = 0,
point;
for (i = 0; i < points.length; i++) {
point = points[i];
if (point.x < minx || i === 0) {
minx = point.x;
}
if (point.x > maxx || i === 0) {
maxx = point.x;
}
if (point.y < miny || i === 0) {
miny = point.y;
}
if (point.y > maxy || i === 0) {
maxy = point.y;
}
}
this.x = minx;
this.y = miny;
this.width = maxx - minx;
this.height = maxy - miny;
// Allow chaining.
return this;
};
/**
* Checks if rect has min width.
* @method has_min_width
* @return bool true if width is more than 5px.
* @public
*/
this.has_min_width = function() {
return (this.width >= 5);
};
/**
* Checks if rect has min height.
* @method has_min_height
* @return bool true if height is more than 5px.
* @public
*/
this.has_min_height = function() {
return (this.height >= 5);
};
/**
* Set min. width of annotation bound.
* @method set_min_width
* @public
*/
this.set_min_width = function() {
this.width = 5;
};
/**
* Set min. height of annotation bound.
* @method set_min_height
* @public
*/
this.set_min_height = function() {
this.height = 5;
};
};
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.rect = RECT;
@@ -0,0 +1,113 @@
var STAMPPICKER_NAME = "Colourpicker",
STAMPPICKER;
/**
* Provides an in browser PDF editor.
*
* @module moodle-assignfeedback_editpdf-editor
*/
/**
* This is a drop down list of stamps.
*
* @namespace M.assignfeedback_editpdf
* @class stamppicker
* @constructor
* @extends M.assignfeedback_editpdf.dropdown
*/
STAMPPICKER = function(config) {
STAMPPICKER.superclass.constructor.apply(this, [config]);
};
Y.extend(STAMPPICKER, M.assignfeedback_editpdf.dropdown, {
/**
* Initialise the menu.
*
* @method initializer
* @return void
*/
initializer: function(config) {
var stamplist = Y.Node.create('<ul role="menu" class="assignfeedback_editpdf_menu"/>');
// Build a list of stamped buttons.
Y.each(this.get('stamps'), function(stamp) {
var button, listitem, title;
title = M.util.get_string('stamp', 'assignfeedback_editpdf');
button = Y.Node.create('<button><img height="16" width="16" alt="' + title + '" src="' + stamp + '"/></button>');
button.setAttribute('data-stamp', stamp);
button.setAttribute('role', 'menuitem');
button.setStyle('backgroundImage', 'none');
listitem = Y.Node.create('<li/>');
listitem.append(button);
listitem.setAttribute('role', 'none');
stamplist.append(listitem);
}, this);
// Set the call back.
stamplist.delegate('click', this.callback_handler, 'button', this);
stamplist.delegate('key', this.callback_handler, 'down:13', 'button', this);
// Set the accessible header text.
this.set('headerText', M.util.get_string('stamppicker', 'assignfeedback_editpdf'));
// Set the body content.
this.set('bodyContent', stamplist);
STAMPPICKER.superclass.initializer.call(this, config);
},
callback_handler: function(e) {
e.preventDefault();
var callback = this.get('callback'),
callbackcontext = this.get('context'),
bind;
this.hide();
// Call the callback with the specified context.
bind = Y.bind(callback, callbackcontext, e);
bind();
}
}, {
NAME: STAMPPICKER_NAME,
ATTRS: {
/**
* The list of stamps this stamp picker supports.
*
* @attribute stamps
* @type String[] - the stamp filenames.
* @default {}
*/
stamps: {
value: []
},
/**
* The function called when a new stamp is chosen.
*
* @attribute callback
* @type function
* @default null
*/
callback: {
value: null
},
/**
* The context passed to the callback when a stamp is chosen.
*
* @attribute context
* @type Y.Node
* @default null
*/
context: {
value: null
}
}
});
M.assignfeedback_editpdf = M.assignfeedback_editpdf || {};
M.assignfeedback_editpdf.stamppicker = STAMPPICKER;
@@ -0,0 +1,21 @@
{
"moodle-assignfeedback_editpdf-editor": {
"requires": [
"base",
"event",
"node",
"io",
"graphics",
"json",
"event-move",
"event-resize",
"transition",
"querystring-stringify-simple",
"moodle-core-notification-dialog",
"moodle-core-notification-alert",
"moodle-core-notification-warning",
"moodle-core-notification-exception",
"moodle-core-notification-ajaxexception"
]
}
}