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.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.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.iid; 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.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.iid; 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.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.iid; 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\
\n\ \n\ \n\ ' + (this.hotspotSVG.model.name ? this.hotspotSVG.model.name : 'hotspot ' + this.hotspotIndex) + '\n\ \n\ \n\
\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\
  • \n\ ' + lang.ClosePolygon + '\n\
  • \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 () { $(config.selector).html(''); var hotspotsCollection = new HotspotsCollection(), hotspotsSVG = new AdminHotspotsSVG(hotspotsCollection, this); $(config.selector).css('width', this.width).append(hotspotsSVG.render().el); $(config.selector).parent().prepend('\n\
    \n\

    ' + lang.HotspotStatus1 + '

    \n\ \n\
    \n\ '); $(config.selector).parent().prepend('
    '); 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, answersCollection, image) { var self = this; this.el = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); this.hotspotsCollection = hotspotsCollection; this.answersCollection = 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'); $('', { type: 'hidden', name: 'hotspot[' + config.questionId + '][' + hotspot.id + ']' }).val(function () { return [x, y].join(';'); }).appendTo(this.el.parentNode); $('', { type: 'hidden', name: 'choice[' + config.questionId + '][' + hotspot.id + ']' }).val(function () { return [x, y].join(';'); }).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') { //Hack to move correctly the hot spots if there are more than one HS question in same page answerIndex = $(e.target).next().html(); answerIndex = parseInt(answerIndex) - 1; } else if (e.target.tagName === 'text') { //Hack to move correctly the hot spots if there are more than one HS question in same page answerIndex = $(e.target).html(); answerIndex = parseInt(answerIndex) - 1; } 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 () { return [point.x, point.y].join(';'); }); isMoving = false; }); }; var startHotspotsUser = function (questionInfo) { var image = new Image(); image.onload = function () { $(config.selector).html(''); var hotspotsCollection = new HotspotsCollection(), answersCollection = new AnswersCollection(), hotspotsSVG = new UserHotspotsSVG(hotspotsCollection, answersCollection, this); $(config.selector).css('width', this.width).append(hotspotsSVG.render().el); $(config.selector).parent().prepend('\n\
    \n\

    \n\ \n\ \n\

    \n\
    \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); }); $.each(questionInfo.answers, function (index, answerInfo) { var answerModel = new AnswerModel({ x: answerInfo.x, y: answerInfo.y }); answersCollection.add(answerModel); }); $(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 () { $(config.selector).html(''); 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; } $(config.selector).html('\n\ \n\ Loading\n\ '); var xhrQuestion = null; switch (config.for) { case 'admin': xhrQuestion = $.getJSON(config.relPath+'exercise/hotspot_actionscript_admin.as.php', { modifyAnswers: parseInt(config.questionId) }); break; case 'user': xhrQuestion = $.getJSON(config.relPath+'exercise/hotspot_actionscript.as.php', { modifyAnswers: parseInt(config.questionId), exe_id: parseInt(config.exerciseId) }); break; case 'solution': //no break case 'preview': xhrQuestion = $.getJSON(config.relPath+'exercise/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 = $('
    ').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\
    \n\ \n\ \n\ ' + (type === 'delineation' ? lang.Delineation : lang.Oar) + '\n\ \n\ \n\
    \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 = $('