You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1813 lines
62 KiB
1813 lines
62 KiB
window.HotspotQuestion = (function () {
|
|
return function (settings) {
|
|
var HotspotModel = function (attributes) {
|
|
this.attributes = attributes;
|
|
this.id = 0;
|
|
this.name = '';
|
|
|
|
this.changeEvent = null;
|
|
};
|
|
HotspotModel.prototype.set = function (key, value) {
|
|
this.attributes[key] = value;
|
|
|
|
if (this.changeEvent) {
|
|
this.changeEvent(this);
|
|
}
|
|
};
|
|
HotspotModel.prototype.get = function (key) {
|
|
return this.attributes[key];
|
|
};
|
|
HotspotModel.prototype.onChange = function (callback) {
|
|
this.changeEvent = callback;
|
|
};
|
|
HotspotModel.prototype.checkPoint = function (x, y) {
|
|
return false;
|
|
};
|
|
HotspotModel.decode = function () {
|
|
return new this;
|
|
};
|
|
HotspotModel.prototype.encode = function () {
|
|
return '';
|
|
};
|
|
|
|
var SquareModel = function (attributes) {
|
|
HotspotModel.call(this, attributes);
|
|
};
|
|
SquareModel.prototype = Object.create(HotspotModel.prototype);
|
|
SquareModel.prototype.setStartPoint = function (x, y) {
|
|
x = parseInt(x);
|
|
y = parseInt(y);
|
|
|
|
this.set('x', x);
|
|
this.set('y', y);
|
|
};
|
|
SquareModel.prototype.setEndPoint = function (x, y) {
|
|
var startX = this.get('x'),
|
|
startY = this.get('y');
|
|
|
|
x = parseInt(x);
|
|
y = parseInt(y);
|
|
|
|
if (x >= startX) {
|
|
this.set('width', x - startX);
|
|
}
|
|
|
|
if (y >= startY) {
|
|
this.set('height', y - startY);
|
|
}
|
|
};
|
|
SquareModel.prototype.checkPoint = function (x, y) {
|
|
var left = this.get('x'),
|
|
right = this.get('x') + this.get('width'),
|
|
top = this.get('y'),
|
|
bottom = this.get('y') + this.get('height');
|
|
|
|
var xIsValid = x >= left && x <= right,
|
|
yIsValid = y >= top && y <= bottom;
|
|
|
|
return xIsValid && yIsValid;
|
|
};
|
|
SquareModel.decode = function (hotspotInfo) {
|
|
var coords = hotspotInfo.coord.split('|'),
|
|
position = coords[0].split(';'),
|
|
hotspot = new SquareModel({
|
|
x: parseInt(position[0]),
|
|
y: parseInt(position[1]),
|
|
width: parseInt(coords[1]),
|
|
height: parseInt(coords[2])
|
|
});
|
|
|
|
hotspot.id = hotspotInfo.id;
|
|
hotspot.name = hotspotInfo.answer;
|
|
|
|
return hotspot;
|
|
};
|
|
SquareModel.prototype.encode = function () {
|
|
return [
|
|
[
|
|
this.get('x'),
|
|
this.get('y')
|
|
].join(';'),
|
|
this.get('width'),
|
|
this.get('height')
|
|
].join('|');
|
|
};
|
|
|
|
var EllipseModel = function (attributes) {
|
|
HotspotModel.call(this, attributes);
|
|
};
|
|
EllipseModel.prototype = Object.create(HotspotModel.prototype);
|
|
EllipseModel.prototype.setStartPoint = function (x, y) {
|
|
x = parseInt(x);
|
|
y = parseInt(y);
|
|
|
|
this.set('centerX', x);
|
|
this.set('centerY', y);
|
|
};
|
|
EllipseModel.prototype.setEndPoint = function (x, y) {
|
|
var startX = this.get('centerX'),
|
|
startY = this.get('centerY'),
|
|
width = 0,
|
|
height = 0;
|
|
|
|
x = parseInt(x);
|
|
y = parseInt(y);
|
|
|
|
if (x >= startX) {
|
|
width = x - startX;
|
|
|
|
this.set('radiusX', Math.round(width / 2));
|
|
this.set('centerX', startX + this.get('radiusX'));
|
|
}
|
|
|
|
if (y >= startY) {
|
|
height = y - startY;
|
|
|
|
this.set('radiusY', Math.round(height / 2));
|
|
this.set('centerY', startY + this.get('radiusY'));
|
|
}
|
|
};
|
|
EllipseModel.prototype.checkPoint = function (x, y) {
|
|
var dX = x - this.get('centerX'),
|
|
dY = y - this.get('centerY');
|
|
|
|
var dividend = Math.pow(dX, 2) / Math.pow(this.get('radiusX'), 2),
|
|
divider = Math.pow(dY, 2) / Math.pow(this.get('radiusY'), 2);
|
|
|
|
return dividend + divider <= 1;
|
|
};
|
|
EllipseModel.decode = function (hotspotInfo) {
|
|
var coords = hotspotInfo.coord.split('|'),
|
|
center = coords[0].split(';'),
|
|
hotspot = new EllipseModel({
|
|
centerX: parseInt(center[0]),
|
|
centerY: parseInt(center[1]),
|
|
radiusX: parseInt(coords[1]),
|
|
radiusY: parseInt(coords[2])
|
|
});
|
|
|
|
hotspot.id = hotspotInfo.id;
|
|
hotspot.name = hotspotInfo.answer;
|
|
|
|
return hotspot;
|
|
};
|
|
EllipseModel.prototype.encode = function () {
|
|
return [
|
|
[
|
|
this.get('centerX'),
|
|
this.get('centerY')
|
|
].join(';'),
|
|
this.get('radiusX'),
|
|
this.get('radiusY')
|
|
].join('|');
|
|
};
|
|
|
|
var PolygonModel = function (attributes) {
|
|
HotspotModel.call(this, attributes);
|
|
};
|
|
PolygonModel.prototype = Object.create(HotspotModel.prototype);
|
|
PolygonModel.prototype.addPoint = function (x, y) {
|
|
var points = this.get('points');
|
|
|
|
x = parseInt(x);
|
|
y = parseInt(y);
|
|
|
|
points.push([x, y]);
|
|
|
|
this.set('points', points);
|
|
};
|
|
PolygonModel.prototype.checkPoint = function (x, y) {
|
|
var points = this.get('points'),
|
|
isInside = false;
|
|
|
|
for (var i = 0, j = points.length - 1; i < points.length; j = i++) {
|
|
var xi = points[i][0],
|
|
yi = points[i][1],
|
|
xj = points[j][0],
|
|
yj = points[j][1];
|
|
|
|
var intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
|
|
|
|
if (intersect) {
|
|
isInside = !isInside;
|
|
}
|
|
}
|
|
|
|
return isInside;
|
|
};
|
|
PolygonModel.decode = function (hotspotInfo) {
|
|
var pairedPoints = hotspotInfo.coord.split('|'),
|
|
points = [];
|
|
|
|
$.each(pairedPoints, function (index, pair) {
|
|
var point = pair.split(';');
|
|
|
|
points.push([
|
|
parseInt(point[0]),
|
|
parseInt(point[1])
|
|
]);
|
|
});
|
|
|
|
var hotspot = new PolygonModel({
|
|
points: points
|
|
});
|
|
hotspot.id = hotspotInfo.id;
|
|
hotspot.name = hotspotInfo.answer;
|
|
|
|
return hotspot;
|
|
};
|
|
PolygonModel.prototype.encode = function () {
|
|
var pairedPoints = [];
|
|
|
|
this.get('points').forEach(function (point) {
|
|
pairedPoints.push(
|
|
point.join(';')
|
|
);
|
|
});
|
|
|
|
return pairedPoints.join('|');
|
|
};
|
|
|
|
var AnswerModel = function (attributes) {
|
|
this.attributes = attributes;
|
|
this.changeEvent = null;
|
|
};
|
|
AnswerModel.prototype.set = function (key, value) {
|
|
this.attributes[key] = value;
|
|
|
|
if (this.changeEvent) {
|
|
this.changeEvent(this);
|
|
}
|
|
};
|
|
AnswerModel.prototype.get = function (key) {
|
|
return this.attributes[key];
|
|
};
|
|
AnswerModel.prototype.onChange = function (callback) {
|
|
this.changeEvent = callback;
|
|
};
|
|
AnswerModel.decode = function (answerInfo) {
|
|
var coords = answerInfo.split(';');
|
|
|
|
return new AnswerModel({
|
|
x: coords[0],
|
|
y: coords[1]
|
|
});
|
|
};
|
|
|
|
var AnswersCollection = function () {
|
|
this.models = [];
|
|
this.length = 0;
|
|
this.addEvent = null;
|
|
};
|
|
AnswersCollection.prototype.add = function (answerModel) {
|
|
this.models.push(answerModel);
|
|
this.length++;
|
|
|
|
if (this.addEvent) {
|
|
this.addEvent(answerModel);
|
|
}
|
|
};
|
|
AnswersCollection.prototype.get = function (index) {
|
|
return this.models[index];
|
|
};
|
|
AnswersCollection.prototype.onAdd = function (callback) {
|
|
this.addEvent = callback;
|
|
};
|
|
|
|
var HotspotsCollection = function () {
|
|
this.hotspots = [];
|
|
this.length = 0;
|
|
|
|
this.addEvent = null;
|
|
};
|
|
HotspotsCollection.prototype.add = function (hotspot) {
|
|
this.hotspots.push(hotspot);
|
|
this.length++;
|
|
|
|
if (this.addEvent) {
|
|
this.addEvent(hotspot);
|
|
}
|
|
};
|
|
HotspotsCollection.prototype.get = function (index) {
|
|
return this.hotspots[index];
|
|
};
|
|
HotspotsCollection.prototype.set = function (index, newHotspot) {
|
|
this.hotspots[index] = newHotspot;
|
|
};
|
|
HotspotsCollection.prototype.onAdd = function (callback) {
|
|
this.addEvent = callback;
|
|
};
|
|
|
|
var HotspotSVG = function (modelModel, index) {
|
|
var self = this;
|
|
|
|
this.model = modelModel;
|
|
this.hotspotIndex = index;
|
|
|
|
this.el = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
|
|
this.model.onChange(function (hotspotModel) {
|
|
self.render();
|
|
});
|
|
};
|
|
HotspotSVG.prototype.render = function () {
|
|
var newEl = null;
|
|
|
|
if (this.model instanceof SquareModel) {
|
|
newEl = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
newEl.setAttribute('x', this.model.get('x'));
|
|
newEl.setAttribute('y', this.model.get('y'));
|
|
newEl.setAttribute('width', this.model.get('width'));
|
|
newEl.setAttribute('height', this.model.get('height'));
|
|
} else if (this.model instanceof EllipseModel) {
|
|
newEl = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse');
|
|
newEl.setAttribute('cx', this.model.get('centerX'));
|
|
newEl.setAttribute('cy', this.model.get('centerY'));
|
|
newEl.setAttribute('rx', this.model.get('radiusX'));
|
|
newEl.setAttribute('ry', this.model.get('radiusY'));
|
|
} else if (this.model instanceof PolygonModel) {
|
|
var pointsPaired = [];
|
|
|
|
this.model.get('points').forEach(function (point) {
|
|
pointsPaired.push(point.join(','));
|
|
});
|
|
|
|
newEl = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
|
|
newEl.setAttribute(
|
|
'points',
|
|
pointsPaired.join(' ')
|
|
);
|
|
}
|
|
|
|
newEl.setAttribute('class', 'hotspot-' + this.hotspotIndex);
|
|
|
|
if (this.el.parentNode) {
|
|
this.el.parentNode.replaceChild(newEl, this.el);
|
|
}
|
|
|
|
this.el = newEl;
|
|
|
|
return this;
|
|
};
|
|
|
|
var AnswerSVG = function (answerModel, index) {
|
|
var self = this;
|
|
|
|
this.model = answerModel;
|
|
this.answerIndex = index;
|
|
|
|
this.circleEl = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
this.textEl = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
|
|
this.model.onChange(function (answerModel) {
|
|
self.render();
|
|
});
|
|
};
|
|
AnswerSVG.prototype.render = function () {
|
|
this.circleEl.setAttribute('cx', this.model.get('x'));
|
|
this.circleEl.setAttribute('cy', this.model.get('y'));
|
|
this.circleEl.setAttribute('r', 15);
|
|
this.circleEl.setAttribute('class', 'hotspot-answer-point');
|
|
|
|
this.textEl.setAttribute('x', this.model.get('x'));
|
|
this.textEl.setAttribute('y', this.model.get('y'));
|
|
this.textEl.setAttribute('dy', 5);
|
|
this.textEl.setAttribute('text-anchor', 'middle');
|
|
this.textEl.setAttribute('class', 'hotspot-answer-text');
|
|
this.textEl.textContent = this.answerIndex + 1;
|
|
|
|
return this;
|
|
};
|
|
|
|
var HotspotSelect = function (index, hotspotsCollection, hotspotSVG) {
|
|
this.hotspotIndex = index;
|
|
this.hotspotsCollection = hotspotsCollection;
|
|
this.hotspotSVG = hotspotSVG;
|
|
|
|
this.el = document.createElement('div');
|
|
this.el.className = 'col-xs-6 col-sm-4 col-md-3 col-lg-2';
|
|
|
|
selectedHotspotIndex = this.hotspotIndex;
|
|
|
|
$('.input-group').removeClass('active');
|
|
};
|
|
HotspotSelect.prototype.render = function () {
|
|
var self = this,
|
|
$el = $(this.el);
|
|
|
|
var template = '\n\
|
|
<div class="input-group hotspot-' + this.hotspotIndex + ' active">\n\
|
|
<span class="input-group-addon" id="hotspot-' + this.hotspotIndex + '">\n\
|
|
<span class="fa fa-square fa-fw" data-hidden="true"></span>\n\
|
|
<span class="sr-only">' + (this.hotspotSVG.model.name ? this.hotspotSVG.model.name : 'hotspot ' + this.hotspotIndex) + '</span>\n\
|
|
</span>\n\
|
|
<select class="form-control" aria-describedby="hotspot-' + this.hotspotIndex + '">\n\
|
|
<option value="square">' + lang.Square + '</option>\n\
|
|
<option value="ellipse">' + lang.Ellipse + '</option>\n\
|
|
<option value="polygon">' + lang.Polygon + '</option>\n\
|
|
</select>\n\
|
|
</div>\n\
|
|
';
|
|
$el.html(template);
|
|
|
|
$el.find('select')
|
|
.on('change', function () {
|
|
selectedHotspotIndex = self.hotspotIndex;
|
|
|
|
var newHotspot = null,
|
|
changeEvent = self.hotspotSVG.model.changeEvent;
|
|
|
|
switch (this.value) {
|
|
case 'square':
|
|
newHotspot = new SquareModel({
|
|
x: 0,
|
|
y: 0,
|
|
width: 0,
|
|
height: 0
|
|
});
|
|
$('#hotspot-messages span:not(.fa)').text(lang.HotspotStatus2Other);
|
|
break;
|
|
|
|
case 'ellipse':
|
|
newHotspot = new EllipseModel({
|
|
centerX: 0,
|
|
centerY: 0,
|
|
radiusX: 0,
|
|
radiusY: 0
|
|
});
|
|
$('#hotspot-messages span:not(.fa)').text(lang.HotspotStatus2Other);
|
|
break;
|
|
|
|
case 'polygon':
|
|
newHotspot = new PolygonModel({
|
|
points: []
|
|
});
|
|
$('#hotspot-messages span:not(.fa)').text(lang.HotspotStatus2Polygon);
|
|
break;
|
|
}
|
|
|
|
newHotspot.onChange(changeEvent);
|
|
|
|
self.hotspotsCollection.set(self.hotspotIndex, newHotspot);
|
|
self.hotspotSVG.model = newHotspot;
|
|
})
|
|
.on('focus', function () {
|
|
$('.input-group').removeClass('active');
|
|
|
|
$el.find('.input-group').addClass('active');
|
|
|
|
selectedHotspotIndex = self.hotspotIndex;
|
|
|
|
switch (this.value) {
|
|
case 'square':
|
|
$('#hotspot-messages span:not(.fa)').text(lang.HotspotStatus2Other);
|
|
break;
|
|
|
|
case 'ellipse':
|
|
$('#hotspot-messages span:not(.fa)').text(lang.HotspotStatus2Other);
|
|
break;
|
|
|
|
case 'polygon':
|
|
$('#hotspot-messages span:not(.fa)').text(lang.HotspotStatus2Polygon);
|
|
break;
|
|
}
|
|
})
|
|
.val(function () {
|
|
if (self.hotspotSVG.model instanceof SquareModel) {
|
|
$('#hotspot-messages span:not(.fa)').text(lang.HotspotStatus2Other);
|
|
|
|
return 'square';
|
|
}
|
|
|
|
if (self.hotspotSVG.model instanceof EllipseModel) {
|
|
$('#hotspot-messages span:not(.fa)').text(lang.HotspotStatus2Other);
|
|
|
|
return 'ellipse';
|
|
}
|
|
|
|
if (self.hotspotSVG.model instanceof PolygonModel) {
|
|
$('#hotspot-messages span:not(.fa)').text(lang.HotspotStatus2Polygon);
|
|
|
|
return 'polygon';
|
|
}
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
var ContextMenu = function () {
|
|
this.el = document.createElement('ul');
|
|
|
|
$(this.el).addClass('dropdown-menu').attr('id', "hotspot-context-menu");
|
|
|
|
this.hideEvent = null;
|
|
};
|
|
ContextMenu.prototype.onHide = function (callback) {
|
|
this.hideEvent = callback;
|
|
};
|
|
ContextMenu.prototype.render = function () {
|
|
var self = this,
|
|
template = '\n\
|
|
<li>\n\
|
|
<a href="#">' + lang.ClosePolygon + '</a>\n\
|
|
</li>\n\
|
|
';
|
|
|
|
$(this.el).html(template);
|
|
|
|
$(this.el).find('a').on('click', function (e) {
|
|
e.preventDefault();
|
|
|
|
if (self.hideEvent) {
|
|
self.hideEvent(e);
|
|
}
|
|
|
|
$(self.el).hide();
|
|
});
|
|
|
|
return this;
|
|
};
|
|
ContextMenu.prototype.show = function (x, y) {
|
|
$(this.el).css({left: x, top: y}).show();
|
|
};
|
|
|
|
var AdminHotspotsSVG = function (hotspotsCollection, image) {
|
|
var self = this;
|
|
|
|
this.el = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
this.collection = hotspotsCollection;
|
|
this.image = image;
|
|
|
|
this.collection.onAdd(function (hotspotModel) {
|
|
self.renderHotspot(hotspotModel);
|
|
});
|
|
};
|
|
AdminHotspotsSVG.prototype.render = function () {
|
|
this.el.setAttribute('version', '1.1');
|
|
this.el.setAttribute('viewBox', '0 0 ' + this.image.width + ' ' + this.image.height);
|
|
|
|
var imageSvg = document.createElementNS('http://www.w3.org/2000/svg', 'image');
|
|
imageSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'href', this.image.src);
|
|
imageSvg.setAttribute('width', this.image.width);
|
|
imageSvg.setAttribute('height', this.image.height);
|
|
|
|
this.el.appendChild(imageSvg);
|
|
|
|
this.setEvents();
|
|
|
|
return this;
|
|
};
|
|
AdminHotspotsSVG.prototype.renderHotspot = function (hotspot) {
|
|
var hotspotIndex = this.collection.length - 1,
|
|
hotspotSVG = new HotspotSVG(hotspot, hotspotIndex);
|
|
|
|
this.el.appendChild(
|
|
hotspotSVG.render().el
|
|
);
|
|
|
|
var hotspotSelect = new HotspotSelect(hotspotIndex, this.collection, hotspotSVG);
|
|
|
|
$(config.selector).parent().find('.row').append(
|
|
hotspotSelect.render().el
|
|
);
|
|
};
|
|
AdminHotspotsSVG.prototype.setEvents = function () {
|
|
var self = this,
|
|
$el = $(this.el);
|
|
isDrawing = false;
|
|
|
|
var startPoint = {
|
|
x: 0,
|
|
y: 0
|
|
};
|
|
|
|
$el.on('dragstart', function (e) {
|
|
e.preventDefault();
|
|
})
|
|
.on('mousedown', function (e) {
|
|
e.preventDefault();
|
|
|
|
if (e.button > 0) {
|
|
return;
|
|
}
|
|
|
|
if (self.collection.length <= 0) {
|
|
return;
|
|
}
|
|
|
|
var currentHotspot = self.collection.get(selectedHotspotIndex);
|
|
|
|
if (!currentHotspot) {
|
|
return;
|
|
}
|
|
|
|
startPoint = getPointOnImage(self.el, e.clientX, e.clientY);
|
|
|
|
if (currentHotspot instanceof SquareModel) {
|
|
isDrawing = true;
|
|
|
|
currentHotspot.set('x', startPoint.x);
|
|
currentHotspot.set('y', startPoint.y);
|
|
currentHotspot.set('width', 0);
|
|
currentHotspot.set('height', 0);
|
|
|
|
return;
|
|
}
|
|
|
|
if (currentHotspot instanceof EllipseModel) {
|
|
isDrawing = true;
|
|
|
|
currentHotspot.set('centerX', 0);
|
|
currentHotspot.set('centerY', 0);
|
|
currentHotspot.set('radiusX', 0);
|
|
currentHotspot.set('radiusY', 0);
|
|
return;
|
|
}
|
|
})
|
|
.on('mousemove', function (e) {
|
|
e.preventDefault();
|
|
|
|
if (self.collection.length <= 0) {
|
|
return;
|
|
}
|
|
|
|
if (!isDrawing) {
|
|
return;
|
|
}
|
|
|
|
var currentHotspot = self.collection.get(selectedHotspotIndex),
|
|
currentPoint = getPointOnImage(self.el, e.clientX, e.clientY);
|
|
|
|
if (!currentHotspot) {
|
|
return;
|
|
}
|
|
|
|
if (currentHotspot instanceof SquareModel) {
|
|
if (startPoint.x < currentPoint.x) {
|
|
currentHotspot.set('width', currentPoint.x - startPoint.x);
|
|
} else {
|
|
currentHotspot.set('x', currentPoint.x);
|
|
currentHotspot.set('width', startPoint.x - currentPoint.x);
|
|
}
|
|
|
|
if (startPoint.y < currentPoint.y) {
|
|
currentHotspot.set('height', currentPoint.y - startPoint.y);
|
|
} else {
|
|
currentHotspot.set('y', currentPoint.y);
|
|
currentHotspot.set('height', startPoint.y - currentPoint.y);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (currentHotspot instanceof EllipseModel) {
|
|
var width = 0,
|
|
height = 0;
|
|
|
|
if (startPoint.x < currentPoint.x) {
|
|
width = currentPoint.x - startPoint.x;
|
|
|
|
currentHotspot.set('radiusX', Math.round(width / 2));
|
|
currentHotspot.set('centerX', startPoint.x + currentHotspot.get('radiusX'));
|
|
} else {
|
|
width = startPoint.x - currentPoint.x;
|
|
|
|
currentHotspot.set('radiusX', Math.round(width / 2));
|
|
currentHotspot.set('centerX', currentPoint.x + currentHotspot.get('radiusX'))
|
|
}
|
|
|
|
if (startPoint.y < currentPoint.y) {
|
|
height = currentPoint.y - startPoint.y;
|
|
|
|
currentHotspot.set('radiusY', Math.round(height / 2));
|
|
currentHotspot.set('centerY', startPoint.y + currentHotspot.get('radiusY'));
|
|
} else {
|
|
height = startPoint.y - currentPoint.y;
|
|
|
|
currentHotspot.set('radiusY', Math.round(height / 2));
|
|
currentHotspot.set('centerY', currentPoint.y + currentHotspot.get('radiusY'));
|
|
}
|
|
|
|
return;
|
|
}
|
|
})
|
|
.on('mouseup', function (e) {
|
|
e.preventDefault();
|
|
|
|
if (e.button > 0) {
|
|
return;
|
|
}
|
|
|
|
if (self.collection.length <= 0) {
|
|
return;
|
|
}
|
|
|
|
if (!isDrawing) {
|
|
return;
|
|
}
|
|
|
|
var currentHotspot = self.collection.get(selectedHotspotIndex),
|
|
hotspotTypeSelector = '[name="hotspot_type[' + (selectedHotspotIndex + 1) + ']"]',
|
|
hotspotCoordSelector = '[name="hotspot_coordinates[' + (selectedHotspotIndex + 1) + ']"]';
|
|
|
|
if (!currentHotspot) {
|
|
return;
|
|
}
|
|
|
|
if (currentHotspot instanceof SquareModel) {
|
|
$(hotspotTypeSelector).val('square');
|
|
$(hotspotCoordSelector).val(currentHotspot.encode());
|
|
|
|
isDrawing = false;
|
|
} else if (currentHotspot instanceof EllipseModel) {
|
|
$(hotspotTypeSelector).val('circle');
|
|
$(hotspotCoordSelector).val(currentHotspot.encode());
|
|
|
|
isDrawing = false;
|
|
}
|
|
})
|
|
.on('click', function (e) {
|
|
e.preventDefault();
|
|
|
|
var currentHotspot = self.collection.get(selectedHotspotIndex),
|
|
currentPoint = getPointOnImage(self.el, e.clientX, e.clientY);
|
|
|
|
if (!currentHotspot) {
|
|
return;
|
|
}
|
|
|
|
if (currentHotspot instanceof PolygonModel) {
|
|
var points = [];
|
|
|
|
if (!isDrawing) {
|
|
isDrawing = true;
|
|
} else {
|
|
points = currentHotspot.get('points');
|
|
}
|
|
|
|
points.push([currentPoint.x, currentPoint.y]);
|
|
|
|
currentHotspot.set('points', points);
|
|
|
|
return;
|
|
}
|
|
})
|
|
.on('contextmenu', function (e) {
|
|
e.preventDefault();
|
|
|
|
var currentPoint = getPointOnImage(self.el, e.clientX, e.clientY),
|
|
currentHotspot = self.collection.get(selectedHotspotIndex),
|
|
hotspotTypeSelector = '[name="hotspot_type[' + (selectedHotspotIndex + 1) + ']"]',
|
|
hotspotCoordSelector = '[name="hotspot_coordinates[' + (selectedHotspotIndex + 1) + ']"]';
|
|
|
|
if (!currentHotspot) {
|
|
return;
|
|
}
|
|
|
|
if (currentHotspot instanceof PolygonModel) {
|
|
contextMenu.show(currentPoint.x, currentPoint.y);
|
|
contextMenu.onHide(function () {
|
|
$(hotspotTypeSelector).val('poly');
|
|
$(hotspotCoordSelector).val(currentHotspot.encode());
|
|
|
|
isDrawing = false;
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
var startHotspotsAdmin = function (questionInfo) {
|
|
var image = new Image();
|
|
image.onload = function () {
|
|
var hotspotsCollection = new HotspotsCollection(),
|
|
hotspotsSVG = new AdminHotspotsSVG(hotspotsCollection, this);
|
|
|
|
$(config.selector).css('width', this.width).append(hotspotsSVG.render().el);
|
|
|
|
$(config.selector).parent().prepend('\n\
|
|
<div id="hotspot-messages" class="alert alert-info">\n\
|
|
<h4><span class="fa fa-info-circle" aria-hidden="true"></span> ' + lang.HotspotStatus1 + '</h4>\n\
|
|
<span></span>\n\
|
|
</div>\n\
|
|
');
|
|
|
|
$(config.selector).parent().prepend('<div class="row"></div>');
|
|
|
|
contextMenu = new ContextMenu();
|
|
|
|
$(config.selector).append(
|
|
contextMenu.render().el
|
|
);
|
|
|
|
$.each(questionInfo.hotspots, function (index, hotspotInfo) {
|
|
var hotspot = null;
|
|
|
|
switch (hotspotInfo.type) {
|
|
case 'square':
|
|
default:
|
|
hotspot = SquareModel.decode(hotspotInfo);
|
|
break;
|
|
|
|
case 'circle':
|
|
hotspot = EllipseModel.decode(hotspotInfo);
|
|
break;
|
|
|
|
case 'poly':
|
|
hotspot = PolygonModel.decode(hotspotInfo);
|
|
break;
|
|
}
|
|
|
|
hotspotsCollection.add(hotspot);
|
|
});
|
|
};
|
|
image.src = questionInfo.image;
|
|
|
|
lang = questionInfo.lang;
|
|
};
|
|
|
|
var UserHotspotsSVG = function (hotspotsCollection, image) {
|
|
var self = this;
|
|
|
|
this.el = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
this.hotspotsCollection = hotspotsCollection;
|
|
this.answersCollection = new AnswersCollection();
|
|
this.image = image;
|
|
|
|
this.answersCollection.onAdd(function (answerModel) {
|
|
self.renderAnswer(answerModel);
|
|
});
|
|
};
|
|
UserHotspotsSVG.prototype.render = function () {
|
|
this.el.setAttribute('version', '1.1');
|
|
this.el.setAttribute('viewBox', '0 0 ' + this.image.width + ' ' + this.image.height);
|
|
|
|
var imageSvg = document.createElementNS('http://www.w3.org/2000/svg', 'image');
|
|
imageSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'href', this.image.src);
|
|
imageSvg.setAttribute('width', this.image.width);
|
|
imageSvg.setAttribute('height', this.image.height);
|
|
|
|
this.el.appendChild(imageSvg);
|
|
|
|
this.setEvents();
|
|
|
|
return this;
|
|
};
|
|
UserHotspotsSVG.prototype.renderAnswer = function (answerModel) {
|
|
var answerSVG = new AnswerSVG(answerModel, this.answersCollection.length - 1);
|
|
answerSVG.render();
|
|
|
|
this.el.appendChild(answerSVG.circleEl);
|
|
this.el.appendChild(answerSVG.textEl);
|
|
|
|
var hotspot = this.hotspotsCollection.get(this.answersCollection.length - 1),
|
|
x = answerModel.get('x'),
|
|
y = answerModel.get('y');
|
|
|
|
$('<input>', {
|
|
type: 'hidden',
|
|
name: 'hotspot[' + config.questionId + '][' + hotspot.id + ']'
|
|
}).val(function () {
|
|
return [x, y].join(';');
|
|
}).appendTo(this.el.parentNode);
|
|
|
|
$('<input>', {
|
|
type: 'hidden',
|
|
name: 'choice[' + config.questionId + '][' + hotspot.id + ']'
|
|
}).val(function () {
|
|
if (hotspot.checkPoint(x, y)) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}).appendTo(this.el.parentNode);
|
|
};
|
|
UserHotspotsSVG.prototype.setEvents = function () {
|
|
var self = this,
|
|
$el = $(this.el);
|
|
|
|
var isMoving = false,
|
|
answerIndex = null,
|
|
hotspot = null,
|
|
point = {};
|
|
|
|
$el
|
|
.on('dragstart', function (e) {
|
|
e.preventDefault();
|
|
})
|
|
.on('click', function (e) {
|
|
e.preventDefault();
|
|
|
|
if (isMoving) {
|
|
return;
|
|
}
|
|
|
|
if (self.answersCollection.length >= self.hotspotsCollection.length) {
|
|
return;
|
|
}
|
|
|
|
var point = getPointOnImage(self.el, e.clientX, e.clientY);
|
|
|
|
var answerModel = new AnswerModel({
|
|
x: point.x,
|
|
y: point.y
|
|
});
|
|
|
|
self.answersCollection.add(answerModel);
|
|
|
|
if (self.answersCollection.length === self.hotspotsCollection.length) {
|
|
$(config.selector).parent()
|
|
.find('#hotspot-messages-' + config.questionId + ' span:not(.fa)').text(
|
|
lang.HotspotExerciseFinished
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
$(config.selector).parent()
|
|
.find('#hotspot-messages-' + config.questionId + ' span:not(.fa)').text(
|
|
lang.NextAnswer + ' ' + self.hotspotsCollection.get(
|
|
self.answersCollection.length
|
|
).name
|
|
);
|
|
|
|
isMoving = false;
|
|
})
|
|
.on('mousedown', 'circle, text', function (e) {
|
|
e.preventDefault();
|
|
|
|
isMoving = true;
|
|
|
|
if (e.target.tagName === 'circle') {
|
|
answerIndex = $(e.target).index('circle');
|
|
} else if (e.target.tagName === 'text') {
|
|
answerIndex = $(e.target).index('text');
|
|
}
|
|
|
|
hotspot = self.hotspotsCollection.get(answerIndex);
|
|
})
|
|
.on('mousemove', function (e) {
|
|
if (!isMoving) {
|
|
return;
|
|
}
|
|
|
|
e.preventDefault();
|
|
|
|
point = getPointOnImage(self.el, e.clientX, e.clientY);
|
|
|
|
self.answersCollection.get(answerIndex).set('x', point.x);
|
|
self.answersCollection.get(answerIndex).set('y', point.y);
|
|
})
|
|
.on('mouseup', function (e) {
|
|
if (!isMoving) {
|
|
return;
|
|
}
|
|
|
|
e.preventDefault();
|
|
|
|
$('[name="hotspot[' + config.questionId + '][' + hotspot.id + ']"]').val(function () {
|
|
return [point.x, point.y].join(';');
|
|
});
|
|
$('[name="choice[' + config.questionId + '][' + hotspot.id + ']"]').val(function () {
|
|
if (hotspot.checkPoint(point.x, point.y)) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
});
|
|
|
|
isMoving = false;
|
|
});
|
|
};
|
|
|
|
var startHotspotsUser = function (questionInfo) {
|
|
var image = new Image();
|
|
image.onload = function () {
|
|
var hotspotsCollection = new HotspotsCollection(),
|
|
hotspotsSVG = new UserHotspotsSVG(hotspotsCollection, this);
|
|
|
|
$(config.selector).css('width', this.width).append(hotspotsSVG.render().el);
|
|
|
|
$(config.selector).parent().prepend('\n\
|
|
<div id="hotspot-messages-' + config.questionId + '" class="alert alert-info">\n\
|
|
<h4>\n\
|
|
<span class="fa fa-info-circle" aria-hidden="true"></span>\n\
|
|
<span></span>\n\
|
|
</h4>\n\
|
|
</div>\n\
|
|
');
|
|
|
|
$.each(questionInfo.hotspots, function (index, hotspotInfo) {
|
|
var hotspot = null;
|
|
|
|
switch (hotspotInfo.type) {
|
|
case 'square':
|
|
default:
|
|
hotspot = SquareModel.decode(hotspotInfo);
|
|
break;
|
|
|
|
case 'circle':
|
|
hotspot = EllipseModel.decode(hotspotInfo);
|
|
break;
|
|
|
|
case 'poly':
|
|
hotspot = PolygonModel.decode(hotspotInfo);
|
|
break;
|
|
}
|
|
|
|
hotspotsCollection.add(hotspot);
|
|
});
|
|
|
|
$(config.selector).parent().find('#hotspot-messages-' + config.questionId + ' span:not(.fa)')
|
|
.text(
|
|
lang.NextAnswer + ' ' + hotspotsCollection.get(0).name
|
|
);
|
|
};
|
|
image.src = questionInfo.image;
|
|
|
|
lang = questionInfo.lang;
|
|
};
|
|
|
|
var SolutionHotspotsSVG = function (hotspotsCollection, answersCollection, image) {
|
|
this.hotspotsCollection = hotspotsCollection;
|
|
this.answersCollection = answersCollection;
|
|
this.image = image;
|
|
this.el = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
|
|
var self = this;
|
|
|
|
this.hotspotsCollection.onAdd(function (hotspotModel) {
|
|
self.renderHotspot(hotspotModel);
|
|
});
|
|
|
|
this.answersCollection.onAdd(function (answerModel) {
|
|
self.renderAnswer(answerModel);
|
|
});
|
|
};
|
|
SolutionHotspotsSVG.prototype.render = function () {
|
|
this.el.setAttribute('version', '1.1');
|
|
this.el.setAttribute('viewBox', '0 0 ' + this.image.width + ' ' + this.image.height);
|
|
|
|
var imageSvg = document.createElementNS('http://www.w3.org/2000/svg', 'image');
|
|
imageSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'href', this.image.src);
|
|
imageSvg.setAttribute('width', this.image.width);
|
|
imageSvg.setAttribute('height', this.image.height);
|
|
|
|
this.el.appendChild(imageSvg);
|
|
|
|
return this;
|
|
};
|
|
SolutionHotspotsSVG.prototype.renderHotspot = function (hotspotModel) {
|
|
var hotspotIndex = this.hotspotsCollection.length - 1,
|
|
hotspotSVG = new HotspotSVG(hotspotModel, hotspotIndex);
|
|
|
|
this.el.appendChild(
|
|
hotspotSVG.render().el
|
|
);
|
|
|
|
return this;
|
|
};
|
|
SolutionHotspotsSVG.prototype.renderAnswer = function (answerModel) {
|
|
var pointSVG = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
pointSVG.setAttribute('cx', answerModel.get('x'));
|
|
pointSVG.setAttribute('cy', answerModel.get('y'));
|
|
pointSVG.setAttribute('r', 15);
|
|
pointSVG.setAttribute('class', 'hotspot-answer-point');
|
|
|
|
var textSVG = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
textSVG.setAttribute('x', answerModel.get('x'));
|
|
textSVG.setAttribute('y', answerModel.get('y'));
|
|
textSVG.setAttribute('dy', 5);
|
|
textSVG.setAttribute('font-family', 'sans-serif');
|
|
textSVG.setAttribute('text-anchor', 'middle');
|
|
textSVG.setAttribute('fill', 'white');
|
|
textSVG.textContent = this.answersCollection.length;
|
|
|
|
this.el.appendChild(pointSVG);
|
|
this.el.appendChild(textSVG);
|
|
|
|
return this;
|
|
};
|
|
|
|
var startHotspotsSolution = function (questionInfo) {
|
|
var image = new Image();
|
|
image.onload = function () {
|
|
var hotspotsCollection = new HotspotsCollection(),
|
|
answersCollection = new AnswersCollection(),
|
|
hotspotsSVG = new SolutionHotspotsSVG(hotspotsCollection, answersCollection, this);
|
|
|
|
$(config.selector).css('width', this.width).append(hotspotsSVG.render().el);
|
|
|
|
$.each(questionInfo.hotspots, function (index, hotspotInfo) {
|
|
var hotspot = null;
|
|
|
|
switch (hotspotInfo.type) {
|
|
case 'square':
|
|
default:
|
|
hotspot = SquareModel.decode(hotspotInfo);
|
|
break;
|
|
|
|
case 'circle':
|
|
hotspot = EllipseModel.decode(hotspotInfo);
|
|
break;
|
|
|
|
case 'poly':
|
|
hotspot = PolygonModel.decode(hotspotInfo);
|
|
break;
|
|
}
|
|
|
|
hotspotsCollection.add(hotspot);
|
|
});
|
|
|
|
$.each(questionInfo.answers, function (index, answerInfo) {
|
|
var answer = AnswerModel.decode(answerInfo);
|
|
|
|
answersCollection.add(answer);
|
|
});
|
|
};
|
|
image.src = questionInfo.image;
|
|
|
|
lang = questionInfo.lang;
|
|
};
|
|
|
|
var getPointOnImage = function (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 config, lang, selectedHotspotIndex = 0, contextMenu;
|
|
config = $.extend({
|
|
questionId: 0,
|
|
selector: ''
|
|
}, settings);
|
|
|
|
if (!config.questionId || !config.selector) {
|
|
return;
|
|
}
|
|
|
|
var xhrQuestion = null;
|
|
|
|
switch (config.for) {
|
|
case 'admin':
|
|
xhrQuestion = $.getJSON('/main/exercice/hotspot_actionscript_admin.as.php', {
|
|
modifyAnswers: parseInt(config.questionId)
|
|
});
|
|
break;
|
|
|
|
case 'user':
|
|
xhrQuestion = $.getJSON('/main/exercice/hotspot_actionscript.as.php', {
|
|
modifyAnswers: parseInt(config.questionId)
|
|
});
|
|
break;
|
|
|
|
case 'solution':
|
|
//no break
|
|
case 'preview':
|
|
xhrQuestion = $.getJSON('/main/exercice/hotspot_answers.as.php', {
|
|
modifyAnswers: parseInt(config.questionId),
|
|
exe_id: parseInt(config.exerciseId)
|
|
});
|
|
break;
|
|
}
|
|
|
|
$.when(xhrQuestion).done(function (questionInfo) {
|
|
switch (questionInfo.type) {
|
|
case 'admin':
|
|
startHotspotsAdmin(questionInfo);
|
|
break;
|
|
|
|
case 'user':
|
|
startHotspotsUser(questionInfo);
|
|
break;
|
|
|
|
case 'solution':
|
|
//no break
|
|
case 'preview':
|
|
startHotspotsSolution(questionInfo);
|
|
break;
|
|
}
|
|
});
|
|
};
|
|
})();
|
|
|
|
window.DelineationQuestion = (function () {
|
|
var PolygonModel = function (attributes) {
|
|
this.id = 0;
|
|
this.name = '';
|
|
this.attributes = attributes;
|
|
|
|
this.event = null;
|
|
};
|
|
PolygonModel.prototype.set = function (key, value) {
|
|
this.attributes[key] = value;
|
|
|
|
if (this.event) {
|
|
this.event(this);
|
|
}
|
|
};
|
|
PolygonModel.prototype.get = function (key) {
|
|
if (!this.attributes[key]) {
|
|
return;
|
|
}
|
|
|
|
return this.attributes[key];
|
|
};
|
|
PolygonModel.prototype.onChange = function (callback) {
|
|
this.event = callback;
|
|
};
|
|
PolygonModel.prototype.encode = function () {
|
|
var pairedPoints = [];
|
|
|
|
$.each(this.get('points'), function (index, point) {
|
|
pairedPoints.push(
|
|
point.join(';')
|
|
);
|
|
});
|
|
|
|
return pairedPoints.join('|');
|
|
};
|
|
PolygonModel.decode = function (hotspotInfo) {
|
|
var pairedPoints = hotspotInfo.coord.split('|'),
|
|
points = [];
|
|
|
|
$.each(pairedPoints, function (index, pair) {
|
|
var point = pair.split(';');
|
|
|
|
points.push([
|
|
parseInt(point[0]),
|
|
point[1] ? parseInt(point[1]) : 0
|
|
]);
|
|
});
|
|
|
|
var hotspot = null;
|
|
|
|
if (hotspotInfo.type === 'delineation') {
|
|
hotspot = new DelineationModel({
|
|
points: points
|
|
});
|
|
} else if (hotspotInfo.type === 'oar') {
|
|
hotspot = new OarModel({
|
|
points: points
|
|
});
|
|
}
|
|
|
|
if (!hotspot) {
|
|
return;
|
|
}
|
|
|
|
hotspot.id = hotspotInfo.id;
|
|
hotspot.name = hotspotInfo.answer;
|
|
|
|
return hotspot;
|
|
};
|
|
|
|
var DelineationModel = function (attributes) {
|
|
PolygonModel.call(this, attributes);
|
|
};
|
|
DelineationModel.prototype = Object.create(PolygonModel.prototype);
|
|
|
|
var OarModel = function (attributes) {
|
|
PolygonModel.call(this, attributes);
|
|
};
|
|
OarModel.prototype = Object.create(PolygonModel.prototype);
|
|
|
|
var AnswerModel = function (attributes) {
|
|
PolygonModel.call(this, attributes);
|
|
};
|
|
AnswerModel.prototype = Object.create(PolygonModel.prototype);
|
|
AnswerModel.prototype.encode = function () {
|
|
var pairedPoints = [];
|
|
|
|
$.each(this.get('points'), function (index, point) {
|
|
pairedPoints.push(point.join(';'));
|
|
});
|
|
|
|
return pairedPoints.join('/');
|
|
};
|
|
|
|
var PolygonCollection = function () {
|
|
this.models = [];
|
|
this.length = 0;
|
|
|
|
this.event = null;
|
|
};
|
|
PolygonCollection.prototype.add = function (model) {
|
|
this.models.push(model);
|
|
this.length++;
|
|
|
|
if (this.event) {
|
|
this.event(model);
|
|
}
|
|
};
|
|
PolygonCollection.prototype.get = function (index) {
|
|
return this.models[index];
|
|
};
|
|
PolygonCollection.prototype.set = function (index, model) {
|
|
this.models[index] = model;
|
|
};
|
|
PolygonCollection.prototype.onAdd = function (callback) {
|
|
this.event = callback;
|
|
};
|
|
|
|
var PolygonSvg = function (polygonModel) {
|
|
this.el = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
|
|
this.model = polygonModel;
|
|
this.model.onChange(function () {
|
|
self.render();
|
|
});
|
|
|
|
var self = this,
|
|
$el = $(this.el);
|
|
|
|
this.render = function () {
|
|
var newEl = document.createElementNS('http://www.w3.org/2000/svg', 'polygon'),
|
|
pointsPaired = [];
|
|
|
|
$.each(this.model.get('points'), function (index, point) {
|
|
pointsPaired.push(point.join(','));
|
|
});
|
|
|
|
newEl.setAttributeNS(null, 'points', pointsPaired.join(' '));
|
|
newEl.setAttributeNS(null, 'class', 'hotspot-' + this.model.id);
|
|
|
|
if ($el.parent().length > 0) {
|
|
$el.replaceWith(newEl);
|
|
}
|
|
|
|
if (this.el.parentNode) {
|
|
this.el.parentNode.replaceChild(newEl, this.el);
|
|
}
|
|
|
|
this.el = newEl;
|
|
|
|
return this;
|
|
};
|
|
};
|
|
|
|
var HotspotSelect = function (polygonModel) {
|
|
this.el = $('<div>').addClass('col-xs-6 col-sm-4 col-md-3 col-lg-2').get(0);
|
|
this.model = polygonModel;
|
|
|
|
selectedPolygonIndex = this.model.id;
|
|
|
|
var self = this,
|
|
$el = $(this.el);
|
|
|
|
this.render = function () {
|
|
var type = this.model instanceof OarModel ? 'oar' : 'delineation';
|
|
|
|
var template = '\n\
|
|
<div class="input-group hotspot-' + this.model.id + ' active">\n\
|
|
<span class="input-group-addon" id="hotspot-' + this.model.id + '">\n\
|
|
<span class="fa fa-square fa-fw" data-hidden="true"></span>\n\
|
|
<span class="sr-only">' + (type === 'delineation' ? lang.Delineation : lang.Oar) + '</span>\n\
|
|
</span>\n\
|
|
<select class="form-control" aria-describedby="hotspot-' + this.hotspotIndex + '">\n\
|
|
<option selected>' + (type === 'delineation' ? lang.Delineation : lang.Oar) + '</option>\n\
|
|
</select>\n\
|
|
</div>\n\
|
|
';
|
|
|
|
$el.html(template);
|
|
|
|
$el.find('select')
|
|
.on('focus', function () {
|
|
$('.input-group').removeClass('active');
|
|
|
|
$el.find('.input-group').addClass('active');
|
|
|
|
selectedPolygonIndex = self.model.id;
|
|
});
|
|
|
|
return this;
|
|
};
|
|
};
|
|
|
|
var ContextMenu = function () {
|
|
this.el = $('<ul>', {
|
|
id: 'hotspot-context-menu'
|
|
}).addClass('dropdown-menu').get(0);
|
|
|
|
var self = this,
|
|
$el = $(this.el);
|
|
|
|
this.onHide = function (callback) {
|
|
$(this).on('hide', function () {
|
|
callback();
|
|
});
|
|
};
|
|
|
|
this.render = function () {
|
|
var template = '\n\
|
|
<li>\n\
|
|
<a href="#">' + lang.CloseDelineation + '</a>\n\
|
|
</li>\n\
|
|
';
|
|
|
|
$el.html(template);
|
|
|
|
$el.find('a')
|
|
.on('click', function (e) {
|
|
e.preventDefault();
|
|
|
|
$(self).trigger('hide');
|
|
|
|
$el.hide();
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
this.show = function (x, y) {
|
|
$el.css({
|
|
left: x,
|
|
top: y
|
|
}).show();
|
|
};
|
|
};
|
|
|
|
var AdminSvg = function (polygonCollection, image) {
|
|
this.collection = polygonCollection;
|
|
this.el = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
|
|
var self = this,
|
|
$el = $(this.el);
|
|
|
|
this.collection.onAdd(function (polygonModel) {
|
|
self.renderPolygon(polygonModel);
|
|
});
|
|
|
|
this.render = function () {
|
|
var imageSvg = document.createElementNS('http://www.w3.org/2000/svg', 'image');
|
|
imageSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', image.src);
|
|
imageSvg.setAttributeNS(null, 'width', image.width);
|
|
imageSvg.setAttributeNS(null, 'height', image.height);
|
|
|
|
this.el.setAttributeNS(null, 'version', '1.1');
|
|
this.el.setAttributeNS(null, 'viewBox', '0 0 ' + image.width + ' ' + image.height);
|
|
this.el.appendChild(imageSvg);
|
|
|
|
var isDrawing = false;
|
|
|
|
var contextMenu = new ContextMenu();
|
|
contextMenu.onHide(function () {
|
|
var currentHotspot = self.collection.get(selectedPolygonIndex);
|
|
|
|
$('[name="hotspot_coordinates[' + (currentHotspot.id + 1) + ']"]').val(
|
|
currentHotspot.encode()
|
|
);
|
|
|
|
isDrawing = false;
|
|
});
|
|
|
|
$el.on({
|
|
'dragstart': function (e) {
|
|
e.preventDefault();
|
|
},
|
|
'click': function (e) {
|
|
e.preventDefault();
|
|
|
|
var currentPoint = getPointOnImage(self.el, e.clientX, e.clientY),
|
|
points = [];
|
|
|
|
if (!isDrawing) {
|
|
isDrawing = true;
|
|
} else {
|
|
points = self.collection.get(selectedPolygonIndex).get('points');
|
|
}
|
|
|
|
points.push([currentPoint.x, currentPoint.y]);
|
|
|
|
self.collection.get(selectedPolygonIndex).set('points', points);
|
|
},
|
|
'contextmenu': function (e) {
|
|
e.preventDefault();
|
|
|
|
if (!contextMenu.el.parentNode) {
|
|
$el.parent().append(contextMenu.render().el);
|
|
}
|
|
|
|
var currentPoint = getPointOnImage(self.el, e.clientX, e.clientY);
|
|
|
|
contextMenu.show(currentPoint.x, currentPoint.y);
|
|
}
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
this.renderPolygon = function (oarModel) {
|
|
var oarSVG = new PolygonSvg(oarModel);
|
|
|
|
$el.append(oarSVG.render().el);
|
|
|
|
var oarSelect = new HotspotSelect(oarModel);
|
|
|
|
$el.parent().parent().find('.row').append(
|
|
oarSelect.render().el
|
|
);
|
|
|
|
return this;
|
|
};
|
|
};
|
|
|
|
var startAdminSvg = function (questionInfo) {
|
|
var image = new Image();
|
|
image.onload = function () {
|
|
var polygonCollection = new PolygonCollection(),
|
|
adminSvg = new AdminSvg(polygonCollection, image);
|
|
|
|
$(config.selector)
|
|
.css('width', this.width)
|
|
.append(
|
|
adminSvg.render().el
|
|
);
|
|
|
|
$(config.selector).parent().prepend('\n\
|
|
<div id="delineation-messages" class="alert alert-info">\n\
|
|
<h4>\n\
|
|
<span class="fa fa-info-circle" aria-hidden="true"></span>\n\
|
|
<span>' + lang.DelineationStatus1 + '</span>\n\
|
|
</h4>\n\
|
|
</div>\n\
|
|
');
|
|
|
|
$(config.selector).parent().prepend('<div class="row"></div>');
|
|
|
|
$.each(questionInfo.hotspots, function (index, hotspotInfo) {
|
|
$('.input-group').removeClass('active');
|
|
|
|
var polygonModel = PolygonModel.decode(hotspotInfo);
|
|
polygonModel.id = index;
|
|
|
|
polygonCollection.add(polygonModel);
|
|
});
|
|
};
|
|
image.src = questionInfo.image;
|
|
|
|
lang = questionInfo.lang;
|
|
};
|
|
|
|
var UserSvg = function (answerModel, image) {
|
|
this.el = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
this.model = answerModel;
|
|
|
|
var self = this,
|
|
$el = $(this.el);
|
|
|
|
this.render = function () {
|
|
var imageSvg = document.createElementNS('http://www.w3.org/2000/svg', 'image');
|
|
imageSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', image.src);
|
|
imageSvg.setAttributeNS(null, 'width', image.width);
|
|
imageSvg.setAttributeNS(null, 'height', image.height);
|
|
|
|
this.el.setAttributeNS(null, 'version', '1.1');
|
|
this.el.setAttributeNS(null, 'viewBox', '0 0 ' + image.width + ' ' + image.height);
|
|
this.el.appendChild(imageSvg);
|
|
|
|
this.renderDelineation();
|
|
|
|
var isDrawing = false;
|
|
|
|
var contextMenu = new ContextMenu();
|
|
contextMenu.onHide(function () {
|
|
var answerInput = $('hotspot[' + config.questionId + '][1]'),
|
|
choiceInput = $('choice[' + config.questionId + '][1]');
|
|
|
|
if (!answerInput.length) {
|
|
answerInput = $('<input>', {
|
|
type: 'hidden',
|
|
name: 'hotspot[' + config.questionId + '][1]'
|
|
}).insertAfter($el);
|
|
}
|
|
|
|
if (!choiceInput.length) {
|
|
choiceInput = $('<input>', {
|
|
type: 'hidden',
|
|
name: 'choice[' + config.questionId + '][1]'
|
|
}).insertAfter($el);
|
|
}
|
|
|
|
answerInput.val(self.model.encode());
|
|
choiceInput.val(1);
|
|
|
|
isDrawing = false;
|
|
});
|
|
|
|
$el.on({
|
|
'dragstart': function (e) {
|
|
e.preventDefault();
|
|
},
|
|
'click': function (e) {
|
|
e.preventDefault();
|
|
|
|
var currentPoint = getPointOnImage(self.el, e.clientX, e.clientY),
|
|
points = [];
|
|
|
|
if (!isDrawing) {
|
|
isDrawing = true;
|
|
} else {
|
|
points = self.model.get('points');
|
|
}
|
|
|
|
points.push([currentPoint.x, currentPoint.y]);
|
|
|
|
self.model.set('points', points);
|
|
},
|
|
'contextmenu': function (e) {
|
|
e.preventDefault();
|
|
|
|
if (!contextMenu.el.parentNode) {
|
|
$el.parent().append(contextMenu.render().el);
|
|
}
|
|
|
|
var currentPoint = getPointOnImage(self.el, e.clientX, e.clientY);
|
|
|
|
contextMenu.show(currentPoint.x, currentPoint.y);
|
|
}
|
|
});
|
|
|
|
return this;
|
|
};
|
|
|
|
this.renderDelineation = function () {
|
|
var delineationSvg = new PolygonSvg(this.model);
|
|
|
|
$el.append(
|
|
delineationSvg.render().el
|
|
);
|
|
};
|
|
};
|
|
|
|
var startUserSvg = function (questionInfo) {
|
|
var image = new Image();
|
|
image.onload = function () {
|
|
var answerModel = new AnswerModel({
|
|
points: []
|
|
}),
|
|
userSvg = new UserSvg(answerModel, image);
|
|
|
|
$(config.selector).parent().prepend('\n\
|
|
<div id="delineation-messages" class="alert alert-info">\n\
|
|
<h4>\n\
|
|
<span class="fa fa-info-circle" aria-hidden="true"></span>\n\
|
|
<span>' + lang.DelineationStatus1 + '</span>\n\
|
|
</h4>\n\
|
|
</div>\n\
|
|
');
|
|
|
|
$(config.selector)
|
|
.css('width', this.width)
|
|
.append(
|
|
userSvg.render().el
|
|
);
|
|
};
|
|
image.src = questionInfo.image;
|
|
|
|
lang = questionInfo.lang;
|
|
};
|
|
|
|
var PreviewSVG = function (polygonCollection, image) {
|
|
this.collection = polygonCollection;
|
|
this.el = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
|
|
var self = this,
|
|
$el = $(this.el);
|
|
|
|
this.collection.onAdd(function (polygonModel) {
|
|
self.renderPolygon(polygonModel);
|
|
});
|
|
|
|
this.render = function () {
|
|
var imageSvg = document.createElementNS('http://www.w3.org/2000/svg', 'image');
|
|
imageSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', image.src);
|
|
imageSvg.setAttributeNS(null, 'width', image.width);
|
|
imageSvg.setAttributeNS(null, 'height', image.height);
|
|
|
|
this.el.setAttributeNS(null, 'version', '1.1');
|
|
this.el.setAttributeNS(null, 'viewBox', '0 0 ' + image.width + ' ' + image.height);
|
|
this.el.appendChild(imageSvg);
|
|
};
|
|
|
|
this.renderPolygon = function (oarModel) {
|
|
var oarSVG = new PolygonSvg(oarModel);
|
|
|
|
$el.append(oarSVG.render().el);
|
|
|
|
return this;
|
|
};
|
|
};
|
|
|
|
var startPreviewSvg = function (questionInfo) {
|
|
var image = new Image();
|
|
image.onload = function () {
|
|
var polygonCollection = new PolygonCollection(),
|
|
previewSvg = new AdminSvg(polygonCollection, image);
|
|
|
|
$(config.selector)
|
|
.css('width', this.width)
|
|
.append(
|
|
previewSvg.render().el
|
|
);
|
|
|
|
$.each(questionInfo.hotspots, function (index, hotspotInfo) {
|
|
var polygonModel = PolygonModel.decode(hotspotInfo);
|
|
polygonModel.id = index;
|
|
|
|
polygonCollection.add(polygonModel);
|
|
});
|
|
};
|
|
image.src = questionInfo.image;
|
|
|
|
lang = questionInfo.lang;
|
|
};
|
|
|
|
var config = {
|
|
questionId: 0,
|
|
exerciseId: 0,
|
|
selector: null,
|
|
for: ''
|
|
},
|
|
lang = {},
|
|
selectedPolygonIndex = -1;
|
|
|
|
var getPointOnImage = function (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)
|
|
};
|
|
};
|
|
|
|
return function (settings) {
|
|
config = $.extend({
|
|
questionId: 0,
|
|
selector: ''
|
|
}, settings);
|
|
|
|
if (!config.questionId || !config.selector) {
|
|
return;
|
|
}
|
|
|
|
var xhrQuestion = null;
|
|
|
|
switch (config.for) {
|
|
case 'admin':
|
|
xhrQuestion = $.getJSON('/main/exercice/hotspot_actionscript_admin.as.php', {
|
|
modifyAnswers: parseInt(config.questionId)
|
|
});
|
|
break;
|
|
|
|
case 'user':
|
|
xhrQuestion = $.getJSON('/main/exercice/hotspot_actionscript.as.php', {
|
|
modifyAnswers: parseInt(config.questionId)
|
|
});
|
|
break;
|
|
|
|
case 'solution':
|
|
//no break
|
|
case 'preview':
|
|
xhrQuestion = $.getJSON('/main/exercice/hotspot_answers.as.php', {
|
|
modifyAnswers: parseInt(config.questionId),
|
|
exe_id: parseInt(config.exerciseId)
|
|
});
|
|
break;
|
|
}
|
|
|
|
$.when(xhrQuestion).done(function (questionInfo) {
|
|
switch (questionInfo.type) {
|
|
case 'admin':
|
|
startAdminSvg(questionInfo);
|
|
break;
|
|
|
|
case 'user':
|
|
startUserSvg(questionInfo);
|
|
break;
|
|
|
|
case 'solution':
|
|
//no break
|
|
case 'preview':
|
|
startPreviewSvg(questionInfo);
|
|
break;
|
|
}
|
|
});
|
|
};
|
|
})();
|
|
|