commit
a33c798c87
@ -0,0 +1,103 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
/** |
||||
* Class Annotation |
||||
* Allow instanciate an object of type HotSpot extending the class question |
||||
* @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com> |
||||
* @package chamilo. |
||||
*/ |
||||
class Annotation extends Question |
||||
{ |
||||
public static $typePicture = 'annotation.png'; |
||||
public static $explanationLangVar = 'Annotation'; |
||||
|
||||
/** |
||||
* Annotation constructor. |
||||
*/ |
||||
public function __construct() |
||||
{ |
||||
parent::__construct(); |
||||
$this->type = ANNOTATION; |
||||
} |
||||
|
||||
public function display() |
||||
{ |
||||
} |
||||
|
||||
/** |
||||
* @param FormValidator $form |
||||
* @param int $fck_config |
||||
*/ |
||||
public function createForm(&$form, $fck_config = 0) |
||||
{ |
||||
parent::createForm($form, $fck_config); |
||||
|
||||
if (isset($_GET['editQuestion'])) { |
||||
$form->addButtonUpdate(get_lang('ModifyExercise'), 'submitQuestion'); |
||||
|
||||
return; |
||||
} |
||||
|
||||
$form->addElement( |
||||
'file', |
||||
'imageUpload', |
||||
array( |
||||
Display::img( |
||||
Display::return_icon('annotation.png', null, null, ICON_SIZE_BIG, false, true) |
||||
), |
||||
get_lang('UploadJpgPicture'), |
||||
) |
||||
); |
||||
|
||||
$form->addButtonSave(get_lang('GoToQuestion'), 'submitQuestion'); |
||||
$form->addRule('imageUpload', get_lang('OnlyImagesAllowed'), 'filetype', array('jpg', 'jpeg', 'png', 'gif')); |
||||
$form->addRule('imageUpload', get_lang('NoImage'), 'uploadedfile'); |
||||
} |
||||
|
||||
/** |
||||
* @param FormValidator $form |
||||
* @param null $objExercise |
||||
* @return null|false |
||||
*/ |
||||
public function processCreation($form, $objExercise = null) |
||||
{ |
||||
$fileInfo = $form->getSubmitValue('imageUpload'); |
||||
$courseInfo = api_get_course_info(); |
||||
|
||||
parent::processCreation($form, $objExercise); |
||||
|
||||
if (!empty($fileInfo['tmp_name'])) { |
||||
$this->uploadPicture($fileInfo['tmp_name'], $fileInfo['name']); |
||||
|
||||
$documentPath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document'; |
||||
$picturePath = $documentPath.'/images'; |
||||
|
||||
// fixed width ang height |
||||
if (!file_exists($picturePath.'/'.$this->picture)) { |
||||
return false; |
||||
} |
||||
|
||||
$this->resizePicture('width', 800); |
||||
$this->save(); |
||||
|
||||
return true; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @param FormValidator $form |
||||
*/ |
||||
function createAnswersForm($form) |
||||
{ |
||||
// nothing |
||||
} |
||||
|
||||
/** |
||||
* @param FormValidator $form |
||||
*/ |
||||
function processAnswersCreation($form) |
||||
{ |
||||
// nothing |
||||
} |
||||
} |
@ -0,0 +1,65 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
use ChamiloSession as Session; |
||||
|
||||
session_cache_limiter("none"); |
||||
|
||||
require_once __DIR__.'/../inc/global.inc.php'; |
||||
|
||||
$questionId = isset($_GET['question_id']) ? intval($_GET['question_id']) : 0; |
||||
$exerciseId = isset($_GET['exe_id']) ? intval($_GET['exe_id']) : 0; |
||||
|
||||
$objQuestion = Question::read($questionId); |
||||
$documentPath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'; |
||||
$picturePath = $documentPath.'/images'; |
||||
$pictureName = $objQuestion->selectPicture(); |
||||
$pictureSize = getimagesize($picturePath.'/'.$objQuestion->selectPicture()); |
||||
$pictureWidth = $pictureSize[0]; |
||||
$pictureHeight = $pictureSize[1]; |
||||
|
||||
$data = [ |
||||
'use' => 'user', |
||||
'image' => [ |
||||
'path' => $objQuestion->selectPicturePath(), |
||||
'width' => $pictureSize[0], |
||||
'height' => $pictureSize[1] |
||||
], |
||||
'answers' => [ |
||||
'paths' => [], |
||||
'texts' => [] |
||||
] |
||||
]; |
||||
|
||||
$attemptList = Event::getAllExerciseEventByExeId($exerciseId); |
||||
|
||||
if (!empty($attemptList) && isset($attemptList[$questionId])) { |
||||
$questionAttempt = $attemptList[$questionId][0]; |
||||
|
||||
if (!empty($questionAttempt['answer'])) { |
||||
$answers = explode('|', $questionAttempt['answer']); |
||||
|
||||
foreach ($answers as $answer) { |
||||
$parts = explode(')(', $answer); |
||||
$type = array_shift($parts); |
||||
|
||||
switch ($type) { |
||||
case 'P': |
||||
$points = []; |
||||
|
||||
foreach ($parts as $partPoint) { |
||||
$points[] = Geometry::decodePoint($partPoint); |
||||
} |
||||
|
||||
$data['answers']['paths'][] = $points; |
||||
break; |
||||
case 'T': |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
header('Content-Type: application/json'); |
||||
|
||||
echo json_encode($data); |
@ -0,0 +1,299 @@ |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
(function (window) { |
||||
/** |
||||
* @param referenceElement Element to get the point |
||||
* @param x MouseEvent's clientX |
||||
* @param y MouseEvent's clientY |
||||
* @returns {{x: number, y: number}} |
||||
*/ |
||||
function getPointOnImage(referenceElement, x, y) { |
||||
var pointerPosition = { |
||||
left: x + window.scrollX, |
||||
top: y + window.scrollY |
||||
}, |
||||
canvasOffset = { |
||||
x: referenceElement.getBoundingClientRect().left + window.scrollX, |
||||
y: referenceElement.getBoundingClientRect().top + window.scrollY |
||||
}; |
||||
|
||||
return { |
||||
x: Math.round(pointerPosition.left - canvasOffset.x), |
||||
y: Math.round(pointerPosition.top - canvasOffset.y) |
||||
}; |
||||
}; |
||||
|
||||
/** |
||||
* @param Object attributes |
||||
* @constructor |
||||
*/ |
||||
var SvgElementModel = function (attributes) { |
||||
this.attributes = attributes; |
||||
this.id = 0; |
||||
this.name = ''; |
||||
|
||||
this.changeEvent = null; |
||||
}; |
||||
SvgElementModel.prototype.set = function (key, value) { |
||||
this.attributes[key] = value; |
||||
|
||||
if (this.changeEvent) { |
||||
this.changeEvent(this); |
||||
} |
||||
}; |
||||
SvgElementModel.prototype.get = function (key) { |
||||
return this.attributes[key]; |
||||
}; |
||||
SvgElementModel.prototype.onChange = function (callback) { |
||||
this.changeEvent = callback; |
||||
}; |
||||
SvgElementModel.decode = function () { |
||||
return new this; |
||||
}; |
||||
SvgElementModel.prototype.encode = function () { |
||||
return ''; |
||||
}; |
||||
|
||||
/** |
||||
* @param Object attributes |
||||
* @constructor |
||||
*/ |
||||
var SvgPathModel = function (attributes) { |
||||
SvgElementModel.call(this, attributes); |
||||
}; |
||||
SvgPathModel.prototype = Object.create(SvgElementModel.prototype); |
||||
SvgPathModel.prototype.addPoint = function (x, y) { |
||||
x = parseInt(x); |
||||
y = parseInt(y); |
||||
|
||||
var points = this.get('points'); |
||||
points.push([x, y]); |
||||
|
||||
this.set('points', points); |
||||
}; |
||||
SvgPathModel.prototype.encode = function () { |
||||
var pairedPoints = []; |
||||
|
||||
this.get('points').forEach(function (point) { |
||||
pairedPoints.push( |
||||
point.join(';') |
||||
); |
||||
}); |
||||
|
||||
return 'P)(' + pairedPoints.join(')('); |
||||
}; |
||||
SvgPathModel.decode = function (pathInfo) { |
||||
var points = []; |
||||
|
||||
$(pathInfo).each(function (i, point) { |
||||
points.push([point.x, point.y]); |
||||
}); |
||||
|
||||
return new SvgPathModel({points: points}); |
||||
}; |
||||
|
||||
/** |
||||
* @param Object model |
||||
* @constructor |
||||
*/ |
||||
var SvgPathView = function (model) { |
||||
var self = this; |
||||
|
||||
this.model = model; |
||||
this.model.onChange(function () { |
||||
self.render(); |
||||
}); |
||||
|
||||
this.el = document.createElementNS('http://www.w3.org/2000/svg', 'path'); |
||||
this.el.setAttribute('fill', 'transparent'); |
||||
this.el.setAttribute('stroke', 'red'); |
||||
this.el.setAttribute('stroke-width', 3); |
||||
}; |
||||
SvgPathView.prototype.render = function () { |
||||
var d = '', |
||||
points = this.model.get('points'); |
||||
|
||||
$.each( |
||||
this.model.get('points'), |
||||
function (i, point) { |
||||
d += (i === 0) ? 'M' : ' L '; |
||||
d += point[0] + ' ' + point[1]; |
||||
} |
||||
); |
||||
|
||||
this.el.setAttribute('d', d); |
||||
|
||||
return this; |
||||
}; |
||||
|
||||
/** |
||||
* @constructor |
||||
*/ |
||||
var PathsCollection = function () { |
||||
this.models = []; |
||||
this.length = 0; |
||||
this.addEvent = null; |
||||
}; |
||||
PathsCollection.prototype.add = function (pathModel) { |
||||
pathModel.id = ++this.length; |
||||
|
||||
this.models.push(pathModel); |
||||
|
||||
if (this.addEvent) { |
||||
this.addEvent(pathModel); |
||||
} |
||||
}; |
||||
PathsCollection.prototype.get = function (index) { |
||||
return this.models[index]; |
||||
}; |
||||
PathsCollection.prototype.onAdd = function (callback) { |
||||
this.addEvent = callback; |
||||
}; |
||||
|
||||
/** |
||||
* @param pathsCollection |
||||
* @param image |
||||
* @param questionId |
||||
* @constructor |
||||
*/ |
||||
var AnnotationCanvasView = function (pathsCollection, image, questionId) { |
||||
var self = this; |
||||
|
||||
this.el = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); |
||||
this.$el = $(this.el); |
||||
|
||||
this.questionId = parseInt(questionId); |
||||
|
||||
this.image = image; |
||||
|
||||
this.pathsCollection = pathsCollection; |
||||
this.pathsCollection.onAdd(function (pathModel) { |
||||
self.renderPath(pathModel); |
||||
}); |
||||
}; |
||||
AnnotationCanvasView.prototype.render = function () { |
||||
this.el.setAttribute('version', '1.1'); |
||||
this.el.setAttribute('viewBox', '0 0 ' + this.image.width + ' ' + this.image.height); |
||||
|
||||
var svgImage = document.createElementNS('http://www.w3.org/2000/svg', 'image'); |
||||
svgImage.setAttributeNS('http://www.w3.org/1999/xlink', 'href', this.image.src); |
||||
svgImage.setAttribute('width', this.image.width); |
||||
svgImage.setAttribute('height', this.image.height); |
||||
|
||||
this.el.appendChild(svgImage); |
||||
this.setEvents(); |
||||
|
||||
return this; |
||||
}; |
||||
AnnotationCanvasView.prototype.setEvents = function () { |
||||
var self = this; |
||||
|
||||
var isMoving = false, |
||||
pathModel = null; |
||||
|
||||
self.$el |
||||
.on('dragstart', function (e) { |
||||
e.preventDefault(); |
||||
}) |
||||
.on('mousedown', function (e) { |
||||
e.preventDefault(); |
||||
|
||||
var point = getPointOnImage(self.el, e.clientX, e.clientY); |
||||
|
||||
pathModel = new SvgPathModel({points: [[point.x, point.y]]}); |
||||
|
||||
self.pathsCollection.add(pathModel); |
||||
|
||||
isMoving = true; |
||||
}) |
||||
.on('mousemove', function (e) { |
||||
e.preventDefault(); |
||||
|
||||
if (!isMoving) { |
||||
return; |
||||
} |
||||
|
||||
var point = getPointOnImage(self.el, e.clientX, e.clientY); |
||||
|
||||
if (!pathModel) { |
||||
return; |
||||
} |
||||
|
||||
pathModel.addPoint(point.x, point.y); |
||||
}) |
||||
.on('mouseup', function (e) { |
||||
e.preventDefault(); |
||||
|
||||
if (!isMoving) { |
||||
return; |
||||
} |
||||
|
||||
$('input[name="choice[' + self.questionId + '][' + pathModel.id + ']"]').val(pathModel.encode()); |
||||
$('input[name="hotspot[' + self.questionId + '][' + pathModel.id + ']"]').val(pathModel.encode()); |
||||
|
||||
pathModel = null; |
||||
|
||||
isMoving = false; |
||||
}); |
||||
}; |
||||
AnnotationCanvasView.prototype.renderPath = function (pathModel) { |
||||
var pathView = new SvgPathView(pathModel); |
||||
|
||||
this.el.appendChild(pathView.render().el); |
||||
|
||||
$('<input>') |
||||
.attr({ |
||||
type: 'hidden', |
||||
name: 'choice[' + this.questionId + '][' + pathModel.id + ']' |
||||
}) |
||||
.val(pathModel.encode()) |
||||
.appendTo(this.el.parentNode); |
||||
|
||||
$('<input>') |
||||
.attr({ |
||||
type: 'hidden', |
||||
name: 'hotspot[' + this.questionId + '][' + pathModel.id + ']' |
||||
}) |
||||
.val(pathModel.encode()) |
||||
.appendTo(this.el.parentNode); |
||||
}; |
||||
|
||||
window.AnnotationQuestion = function (userSettings) { |
||||
var settings = $.extend({ |
||||
questionId: 0, |
||||
exerciseId: 0, |
||||
relPath: '/', |
||||
use: 'user' |
||||
}, userSettings); |
||||
|
||||
var xhrUrl = (settings.use == 'preview') |
||||
? 'exercise/annotation_preview.php' |
||||
: (settings.use == 'admin') |
||||
? 'exercise/annotation_admin.php' |
||||
: 'exercise/annotation_user.php'; |
||||
|
||||
$ |
||||
.getJSON(settings.relPath + xhrUrl, { |
||||
question_id: parseInt(settings.questionId), |
||||
exe_id: parseInt(settings.exerciseId) |
||||
}) |
||||
.done(function (questionInfo) { |
||||
var image = new Image(); |
||||
image.onload = function () { |
||||
var pathsCollection = new PathsCollection(), |
||||
canvas = new AnnotationCanvasView(pathsCollection, this, settings.questionId); |
||||
|
||||
$('#annotation-canvas-' + settings.questionId) |
||||
.css({width: this.width}) |
||||
.html(canvas.render().el); |
||||
|
||||
$.each(questionInfo.answers.paths, function (i, pathInfo) { |
||||
var pathModel = SvgPathModel.decode(pathInfo); |
||||
|
||||
pathsCollection.add(pathModel); |
||||
}); |
||||
}; |
||||
image.src = questionInfo.image.path; |
||||
}); |
||||
}; |
||||
})(window); |
Loading…
Reference in new issue