-
diff --git a/main/inc/lib/javascript/annotation/js/annotation.js b/main/inc/lib/javascript/annotation/js/annotation.js
index fda5f39fa2..ce46bf504f 100644
--- a/main/inc/lib/javascript/annotation/js/annotation.js
+++ b/main/inc/lib/javascript/annotation/js/annotation.js
@@ -22,22 +22,28 @@
var SvgElementModel = function (attributes) {
this.attributes = attributes;
this.id = 0;
- this.name = "";
+ this.questionId = 0;
- this.changeEvent = null;
+ this.changeEvents = [];
+ this.destroyEvents = [];
};
SvgElementModel.prototype.set = function (key, value) {
this.attributes[key] = value;
- if (this.changeEvent) {
- this.changeEvent(this);
- }
+ this.changeEvents.forEach(function (event) {
+ event();
+ });
};
SvgElementModel.prototype.get = function (key) {
return this.attributes[key];
};
- SvgElementModel.prototype.onChange = function (callback) {
- this.changeEvent = callback;
+ SvgElementModel.prototype.destroy = function () {
+ this.destroyEvents.forEach(function (event) {
+ event();
+ });
+ };
+ SvgElementModel.prototype.on = function (event, callback) {
+ this[event + 'Events'].push(callback);
};
SvgElementModel.decode = function () {
return new this();
@@ -46,7 +52,12 @@
return "";
};
- var SvgPathModel = function (attributes) {
+ var SvgPathModel = function (userAttributes) {
+ var attributes = $.extend({
+ color: "#FF0000",
+ points: []
+ }, userAttributes);
+
SvgElementModel.call(this, attributes);
};
SvgPathModel.prototype = Object.create(SvgElementModel.prototype);
@@ -61,6 +72,9 @@
};
SvgPathModel.prototype.encode = function () {
var pairedPoints = [];
+ var typeProperties = [
+ this.get("color"),
+ ];
this.get("points").forEach(function (point) {
pairedPoints.push(
@@ -68,54 +82,64 @@
);
});
- return "P)(" + pairedPoints.join(")(");
+ return "P;" + typeProperties.join(";") + ")(" + pairedPoints.join(")(");
};
SvgPathModel.decode = function (pathInfo) {
- var points = [];
-
- $(pathInfo).each(function (i, point) {
- points.push([point.x, point.y]);
+ pathInfo.points = pathInfo.points.map(function (point) {
+ return [point.x, point.y];
});
- return new SvgPathModel({points: points});
+ return new SvgPathModel(pathInfo);
};
- var TextModel = function (userAttributes) {
+ var SvgTextModel = function (userAttributes) {
var attributes = $.extend({
text: "",
x: 0,
y: 0,
- color: "red",
+ color: "#FF0000",
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");
+ SvgTextModel.prototype = Object.create(SvgElementModel.prototype);
+ SvgTextModel.prototype.encode = function () {
+ var typeProperties = [
+ this.get("color"),
+ this.get("fontSize"),
+ ];
+
+ return "T;" + typeProperties.join(";") + ")(" + 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
- });
+ SvgTextModel.decode = function (textInfo) {
+ return new SvgTextModel(textInfo);
};
- var SvgPathView = function (model) {
+ var SvgElementView = function (model) {
var self = this;
this.model = model;
- this.model.onChange(function () {
+ this.model.on('change', function () {
self.render();
});
+ this.model.on('destroy', function () {
+ self.el.remove();
+ self.model = null;
+ });
+ };
+
+ /**
+ * @param {SvgPathModel} model
+ * @constructor
+ */
+ var SvgPathView = function (model) {
+ SvgElementView.call(this, model);
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 = Object.create(SvgElementView.prototype);
SvgPathView.prototype.render = function () {
var d = "";
@@ -128,39 +152,188 @@
);
this.el.setAttribute("d", d);
+ this.el.setAttribute('stroke', this.model.get('color'));
+ this.el.setAttribute("stroke-width", "3");
return this;
};
- var TextView = function (model) {
- var self = this;
-
- this.model = model;
- this.model.onChange(function () {
- self.render();
- });
+ /**
+ * @param {SvgTextModel} model
+ * @constructor
+ */
+ var SvgTextView = function (model) {
+ SvgElementView.call(this, model);
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 () {
+ SvgTextView.prototype = Object.create(SvgElementView.prototype);
+ SvgTextView.prototype.render = function () {
this.el.setAttribute('x', this.model.get('x'));
this.el.setAttribute('y', this.model.get('y'));
+ this.el.setAttribute('fill', this.model.get('color'));
+ this.el.setAttribute('font-size', this.model.get('fontSize'));
this.el.textContent = this.model.get('text');
return this;
};
+ /**
+ * @param {SvgElementModel} model
+ * @constructor
+ */
+ var ControllerView = function (model) {
+ var self = this;
+
+ this.model = model;
+ this.model.on('change', function () {
+ self.render();
+ });
+ this.model.on('destroy', function () {
+ self.el.remove();
+ self.model = null;
+ });
+
+ var elChoice = (function () {
+ var input = document.createElement('input');
+ input.type = 'hidden';
+ input.name = 'choice[' + self.model.questionId + '][' + self.model.id + ']';
+
+ return input;
+ })();
+
+ var elHotspot = (function () {
+ var input = document.createElement('input');
+ input.type = 'hidden';
+ input.name = 'hotspot[' + self.model.questionId + '][' + self.model.id + ']';
+
+ return input;
+ })();
+
+ var elText = (function () {
+ var input = document.createElement('input');
+ input.type = 'text';
+ input.className = 'form-control';
+ input.disabled = self.model instanceof SvgPathModel;
+ input.value = self.model instanceof SvgTextModel ? self.model.get('text') : '——————————';
+
+ return input;
+ })();
+ elText.addEventListener('change', function () {
+ self.model.set('text', this.value);
+ })
+
+ var txtColor = (function () {
+ var input = document.createElement('input');
+ input.type = 'color';
+ input.value = self.model.get('color');
+ input.style.border = '0 none';
+ input.style.padding = '0';
+ input.style.margin = '0';
+ input.style.width = '26px';
+ input.style.height = '26px';
+ input.style.lineHeight = '28px';
+ input.style.verticalAlign = 'middle';
+
+ return input;
+ })();
+ txtColor.addEventListener('change', function () {
+ self.model.set('color', this.value);
+ })
+
+ var spanAddonColor = (function () {
+ var span = document.createElement('span');
+ span.className = 'input-group-addon';
+ span.style.padding = '0';
+
+ return span;
+ })();
+ spanAddonColor.appendChild(txtColor);
+
+ var txtSize = (function () {
+ var input = document.createElement('input');
+ input.type = 'number';
+ input.value = self.model.get('fontSize');
+ input.step = '1';
+ input.min = '15';
+ input.max = '30';
+ input.style.border = '0 none';
+ input.style.padding = '0 0 0 4px';
+ input.style.margin = '0';
+ input.style.width = '41px';
+ input.style.height = '26px';
+ input.style.lineHeight = '28px';
+ input.style.verticalAlign = 'middle';
+ input.disabled = self.model instanceof SvgPathModel;
+
+ return input;
+ })();
+ txtSize.addEventListener('change', function () {
+ self.model.set('fontSize', this.value);
+ })
+
+ var spanAddonSize = (function () {
+ var span = document.createElement('span');
+ span.className = 'input-group-addon';
+ span.style.padding = '0';
+
+ return span;
+ })();
+ spanAddonSize.appendChild(txtSize);
+
+ var btnRemove = (function () {
+ var button = document.createElement('button');
+ button.type = 'button';
+ button.className = 'btn btn-default';
+ button.innerHTML = '
';
+
+ return button;
+ })();
+ btnRemove.addEventListener('click', function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ self.model.destroy();
+ });
+
+ var spanGroupBtn = (function () {
+ var span = document.createElement('span');
+ span.className = 'input-group-btn';
+
+ return span;
+ })();
+ spanGroupBtn.appendChild(btnRemove);
+
+ this.el = (function () {
+ var div = document.createElement('div');
+ div.className = 'input-group input-group-sm';
+ div.style.marginBottom = '10px';
+
+ return div;
+ })();
+ this.el.appendChild(elText);
+ this.el.appendChild(elHotspot);
+ this.el.appendChild(elChoice);
+ this.el.appendChild(spanAddonColor);
+ this.el.appendChild(spanAddonSize);
+ this.el.appendChild(spanGroupBtn);
+
+ this.render = function () {
+ elChoice.value = this.model.encode();
+ elHotspot.value = this.model.encode();
+
+ return this;
+ }
+ };
+
var ElementsCollection = function () {
this.models = [];
- this.length = 0;
+ this.lastId = 0;
this.addEvent = null;
- this.resetEvent = null;
};
ElementsCollection.prototype.add = function (pathModel) {
- pathModel.id = ++this.length;
+ pathModel.id = ++this.lastId;
this.models.push(pathModel);
@@ -172,18 +345,15 @@
return this.models[index];
};
ElementsCollection.prototype.reset = function () {
- if (this.resetEvent) {
- this.resetEvent();
- }
+ this.models.forEach(function (model) {
+ model.destroy();
+ })
this.models = [];
}
ElementsCollection.prototype.onAdd = function (callback) {
this.addEvent = callback;
};
- ElementsCollection.prototype.onReset = function (callback) {
- this.resetEvent = callback;
- }
var AnnotationCanvasView = function (elementsCollection, image, questionId) {
var self = this;
@@ -208,15 +378,23 @@
this.elementsCollection = elementsCollection;
this.elementsCollection.onAdd(function (pathModel) {
- self.renderElement(pathModel);
- });
- this.elementsCollection.onReset(function () {
- $(self.el.parentNode).children('input').remove();
+ var svgElementView = null;
+
+ if (pathModel instanceof SvgPathModel) {
+ svgElementView = new SvgPathView(pathModel);
+ } else if (pathModel instanceof SvgTextModel) {
+ svgElementView = new SvgTextView(pathModel);
+ } else {
+ return;
+ }
- self.$el.children('text, path').remove();
+ self.el.appendChild(svgElementView.render().el);
- $('#annotation-toolbar-' + self.questionId + ' ul').empty();
- })
+ var controllerView = new ControllerView(pathModel);
+
+ $('#annotation-toolbar-' + self.questionId).append(controllerView.render().el);
+ $(controllerView.el).children('input').eq(0).focus();
+ });
this.$rdbOptions = null;
this.$btnReset = null;
@@ -247,7 +425,8 @@
}
var point = getPointOnImage(self.el, e.clientX, e.clientY);
- elementModel = new TextModel({x: point.x, y: point.y, text: ''});
+ elementModel = new SvgTextModel({x: point.x, y: point.y, text: ''});
+ elementModel.questionId = self.questionId;
self.elementsCollection.add(elementModel);
elementModel = null;
isMoving = false;
@@ -261,6 +440,7 @@
}
elementModel = new SvgPathModel({points: [[point.x, point.y]]});
+ elementModel.questionId = self.questionId;
self.elementsCollection.add(elementModel);
isMoving = true;
})
@@ -291,70 +471,6 @@
self.elementsCollection.reset();
});
};
- 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;
- }
-
- $('
')
- .attr({
- type: 'hidden',
- name: 'choice[' + this.questionId + '][' + elementModel.id + ']'
- })
- .val(elementModel.encode())
- .appendTo(this.el.parentNode);
-
- $('
')
- .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) {
- $('
')
- .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();
- })
- .on('keypress', function (e) {
- if (13 === e.keyCode) {
- e.preventDefault();
-
- elementModel.set('text', this.value);
- }
- })
- .val(elementModel.get('text'))
- .appendTo('#annotation-toolbar-' + this.questionId + ' ul')
- .wrap('
')
- .focus();
- }
- };
window.AnnotationQuestion = function (userSettings) {
$(function () {
@@ -386,12 +502,14 @@
/** @namespace questionInfo.answers.paths */
$.each(questionInfo.answers.paths, function (i, pathInfo) {
var pathModel = SvgPathModel.decode(pathInfo);
+ pathModel.questionId = settings.questionId;
elementsCollection.add(pathModel);
});
/** @namespace questionInfo.answers.texts */
$(questionInfo.answers.texts).each(function (i, textInfo) {
- var textModel = TextModel.decode(textInfo);
+ var textModel = SvgTextModel.decode(textInfo);
+ textModel.questionId = settings.questionId;
elementsCollection.add(textModel);
});
};