123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- /* For licensing terms, see /license.txt */
- (function (window, $) {
- "use strict";
- 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)
- };
- }
- 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 "";
- };
- 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});
- };
- var TextModel = function (userAttributes) {
- var attributes = $.extend({
- text: "",
- x: 0,
- y: 0,
- color: "red",
- fontSize: 20
- }, userAttributes);
- SvgElementModel.call(this, attributes);
- };
- TextModel.prototype = Object.create(SvgElementModel.prototype);
- TextModel.prototype.encode = function () {
- return "T)(" + this.get("text") + ")(" + this.get("x") + ";" + this.get("y");
- };
- TextModel.decode = function (textInfo) {
- return new TextModel({
- text: textInfo.text,
- x: textInfo.x,
- y: textInfo.y
- });
- };
- 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 = "";
- $.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;
- };
- var TextView = function (model) {
- var self = this;
- this.model = model;
- this.model.onChange(function () {
- self.render();
- });
- this.el = document.createElementNS('http://www.w3.org/2000/svg', 'text');
- this.el.setAttribute('fill', this.model.get('color'));
- this.el.setAttribute('font-size', this.model.get('fontSize'));
- this.el.setAttribute('stroke', 'none');
- };
- TextView.prototype.render = function () {
- this.el.setAttribute('x', this.model.get('x'));
- this.el.setAttribute('y', this.model.get('y'));
- this.el.textContent = this.model.get('text');
- return this;
- };
- var ElementsCollection = function () {
- this.models = [];
- this.length = 0;
- this.addEvent = null;
- };
- ElementsCollection.prototype.add = function (pathModel) {
- pathModel.id = ++this.length;
- this.models.push(pathModel);
- if (this.addEvent) {
- this.addEvent(pathModel);
- }
- };
- ElementsCollection.prototype.get = function (index) {
- return this.models[index];
- };
- ElementsCollection.prototype.onAdd = function (callback) {
- this.addEvent = callback;
- };
- var AnnotationCanvasView = function (elementsCollection, image, questionId) {
- var self = this;
- this.questionId = parseInt(questionId);
- this.image = image;
- this.el = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
- this.el.setAttribute('version', '1.1');
- this.el.setAttribute('viewBox', '0 0 ' + this.image.width + ' ' + this.image.height);
- this.el.style.width = this.image.width + 'px';
- this.el.style.height = this.image.height + 'px';
- 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.$el = $(this.el);
- this.elementsCollection = elementsCollection;
- this.elementsCollection.onAdd(function (pathModel) {
- self.renderElement(pathModel);
- });
- this.$rdbOptions = null;
- };
- AnnotationCanvasView.prototype.render = function () {
- this.setEvents();
- this.$rdbOptions = $('[name="' + this.questionId + '-options"]');
- return this;
- };
- AnnotationCanvasView.prototype.setEvents = function () {
- var self = this;
- var isMoving = false,
- elementModel = null;
- self.$el
- .on('dragstart', function (e) {
- e.preventDefault();
- })
- .on('click', function (e) {
- e.preventDefault();
- if ("1" !== self.$rdbOptions.filter(':checked').val()) {
- return;
- }
- var point = getPointOnImage(self.el, e.clientX, e.clientY);
- elementModel = new TextModel({x: point.x, y: point.y, text: ''});
- self.elementsCollection.add(elementModel);
- elementModel = null;
- isMoving = false;
- })
- .on('mousedown', function (e) {
- e.preventDefault();
- var point = getPointOnImage(self.el, e.clientX, e.clientY);
- if (isMoving || "0" !== self.$rdbOptions.filter(':checked').val() || elementModel) {
- return;
- }
- elementModel = new SvgPathModel({points: [[point.x, point.y]]});
- self.elementsCollection.add(elementModel);
- isMoving = true;
- })
- .on('mousemove', function (e) {
- e.preventDefault();
- if (!isMoving || "0" !== self.$rdbOptions.filter(':checked').val() || !elementModel) {
- return;
- }
- var point = getPointOnImage(self.el, e.clientX, e.clientY);
- elementModel.addPoint(point.x, point.y);
- })
- .on('mouseup', function (e) {
- e.preventDefault();
- if (!isMoving || "0" !== self.$rdbOptions.filter(':checked').val() || !elementModel) {
- return;
- }
- elementModel = null;
- isMoving = false;
- });
- };
- AnnotationCanvasView.prototype.renderElement = function (elementModel) {
- var elementView = null,
- self = this;
- if (elementModel instanceof SvgPathModel) {
- elementView = new SvgPathView(elementModel);
- } else if (elementModel instanceof TextModel) {
- elementView = new TextView(elementModel);
- }
- if (!elementView) {
- return;
- }
- $('<input>')
- .attr({
- type: 'hidden',
- name: 'choice[' + this.questionId + '][' + elementModel.id + ']'
- })
- .val(elementModel.encode())
- .appendTo(this.el.parentNode);
- $('<input>')
- .attr({
- type: 'hidden',
- name: 'hotspot[' + this.questionId + '][' + elementModel.id + ']'
- })
- .val(elementModel.encode())
- .appendTo(this.el.parentNode);
- this.el.appendChild(elementView.render().el);
- elementModel.onChange(function () {
- elementView.render();
- $('input[name="choice[' + self.questionId + '][' + elementModel.id + ']"]').val(elementModel.encode());
- $('input[name="hotspot[' + self.questionId + '][' + elementModel.id + ']"]').val(elementModel.encode());
- });
- if (elementModel instanceof TextModel) {
- $('<input>')
- .attr({
- type: 'text',
- name: 'text[' + this.questionId + '][' + elementModel.id + ']'
- })
- .addClass('form-control input-sm')
- .on('change', function (e) {
- elementModel.set('text', this.value);
- e.preventDefault();
- })
- .val(elementModel.get('text'))
- .appendTo('#annotation-toolbar-' + this.questionId + ' ul')
- .wrap('<li class="form-group"></li>')
- .focus();
- }
- };
- window.AnnotationQuestion = function (userSettings) {
- $(document).on('ready', function () {
- var
- settings = $.extend(
- {
- questionId: 0,
- exerciseId: 0,
- relPath: '/'
- },
- userSettings
- ),
- xhrUrl = 'exercise/annotation_user.php',
- $container = $('#annotation-canvas-' + settings.questionId);
- $
- .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 elementsCollection = new ElementsCollection(),
- canvas = new AnnotationCanvasView(elementsCollection, this, settings.questionId);
- $container
- .html(canvas.render().el);
- /** @namespace questionInfo.answers.paths */
- $.each(questionInfo.answers.paths, function (i, pathInfo) {
- var pathModel = SvgPathModel.decode(pathInfo);
- elementsCollection.add(pathModel);
- });
- /** @namespace questionInfo.answers.texts */
- $(questionInfo.answers.texts).each(function (i, textInfo) {
- var textModel = TextModel.decode(textInfo);
- elementsCollection.add(textModel);
- });
- };
- image.src = questionInfo.image.path;
- });
- });
- };
- })(window, window.jQuery);
|