upgrade
This commit is contained in:
783
main/inc/lib/javascript/annotation/js/annotation.js
Normal file
783
main/inc/lib/javascript/annotation/js/annotation.js
Normal file
@@ -0,0 +1,783 @@
|
||||
/* 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)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} attributes
|
||||
* @constructor
|
||||
*/
|
||||
var SvgElementModel = function (attributes) {
|
||||
this.attributes = attributes;
|
||||
this.id = 0;
|
||||
this.questionId = 0;
|
||||
|
||||
this.changeEvents = [];
|
||||
this.destroyEvents = [];
|
||||
};
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {*} value
|
||||
*/
|
||||
SvgElementModel.prototype.set = function (key, value) {
|
||||
this.attributes[key] = value;
|
||||
|
||||
this.changeEvents.forEach(function (event) {
|
||||
event();
|
||||
});
|
||||
};
|
||||
SvgElementModel.prototype.get = function (key) {
|
||||
return this.attributes[key];
|
||||
};
|
||||
SvgElementModel.prototype.destroy = function () {
|
||||
this.destroyEvents.forEach(function (event) {
|
||||
event();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* @param {string} eventName
|
||||
* @param {(SvgElementModel~changeEvents|SvgElementModel~destroyEvents)} callback
|
||||
*/
|
||||
SvgElementModel.prototype.on = function (eventName, callback) {
|
||||
this[eventName + 'Events'].push(callback);
|
||||
};
|
||||
/**
|
||||
* @abstract
|
||||
* @static
|
||||
* @param {Object} info
|
||||
* @returns {SvgElementModel}
|
||||
*/
|
||||
SvgElementModel.decode = function (info) {
|
||||
return new this();
|
||||
};
|
||||
/**
|
||||
* @abstract
|
||||
* @returns {string}
|
||||
*/
|
||||
SvgElementModel.prototype.encode = function () {
|
||||
return "";
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Object} userAttributes
|
||||
* @constructor
|
||||
* @extends SvgElementModel
|
||||
*/
|
||||
var SvgPathModel = function (userAttributes) {
|
||||
var attributes = $.extend({
|
||||
color: "#FF0000",
|
||||
points: []
|
||||
}, userAttributes);
|
||||
|
||||
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 = [];
|
||||
var typeProperties = [
|
||||
this.get("color"),
|
||||
];
|
||||
|
||||
this.get("points").forEach(function (point) {
|
||||
pairedPoints.push(
|
||||
point.join(";")
|
||||
);
|
||||
});
|
||||
|
||||
return "P;" + typeProperties.join(";") + ")(" + pairedPoints.join(")(");
|
||||
};
|
||||
/**
|
||||
* @static
|
||||
* @param {Object} pathInfo
|
||||
* @returns {SvgPathModel}
|
||||
*/
|
||||
SvgPathModel.decode = function (pathInfo) {
|
||||
pathInfo.points = pathInfo.points.map(function (point) {
|
||||
return [point.x, point.y];
|
||||
});
|
||||
|
||||
return new SvgPathModel(pathInfo);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Object} userAttributes
|
||||
* @constructor
|
||||
* @extends SvgElementModel
|
||||
*/
|
||||
var SvgTextModel = function (userAttributes) {
|
||||
var attributes = $.extend({
|
||||
text: "",
|
||||
x: 0,
|
||||
y: 0,
|
||||
color: "#FF0000",
|
||||
fontSize: 20
|
||||
}, userAttributes);
|
||||
|
||||
SvgElementModel.call(this, attributes);
|
||||
};
|
||||
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");
|
||||
};
|
||||
/**
|
||||
* @static
|
||||
* @param {Object} textInfo
|
||||
* @returns {SvgTextModel}
|
||||
*/
|
||||
SvgTextModel.decode = function (textInfo) {
|
||||
return new SvgTextModel(textInfo);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {SvgElementModel} model
|
||||
* @constructor
|
||||
*/
|
||||
var SvgElementView = function (model) {
|
||||
var self = this;
|
||||
|
||||
this.model = model;
|
||||
this.model.on('change', function () {
|
||||
self.render();
|
||||
});
|
||||
this.model.on('destroy', function () {
|
||||
self.el.remove();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {SvgPathModel} model
|
||||
* @constructor
|
||||
* @extends SvgElementView
|
||||
*/
|
||||
var SvgPathView = function (model) {
|
||||
SvgElementView.call(this, model);
|
||||
|
||||
this.el = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
this.el.setAttribute("fill", "transparent");
|
||||
};
|
||||
SvgPathView.prototype = Object.create(SvgElementView.prototype);
|
||||
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);
|
||||
this.el.setAttribute('stroke', this.model.get('color'));
|
||||
this.el.setAttribute("stroke-width", "3");
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {SvgTextModel} model
|
||||
* @constructor
|
||||
* @extends SvgElementView
|
||||
*/
|
||||
var SvgTextView = function (model) {
|
||||
SvgElementView.call(this, model);
|
||||
|
||||
this.el = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||
this.el.setAttribute('stroke', 'none');
|
||||
};
|
||||
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();
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
return input;
|
||||
})();
|
||||
elText.addEventListener('change', function () {
|
||||
commandsHistory.add(new TextElementCommand(self.model, this.value));
|
||||
|
||||
self.model.set('text', this.value);
|
||||
})
|
||||
|
||||
var txtColor = (function () {
|
||||
var input = document.createElement('input');
|
||||
input.type = '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 () {
|
||||
commandsHistory.add(new ColorElementCommand(self.model, this.value));
|
||||
|
||||
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.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 () {
|
||||
commandsHistory.add(new SizeElementCommand(self.model, this.value));
|
||||
|
||||
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 = '<span class="fa fa-trash text-danger" aria-hidden="true"></span>';
|
||||
//
|
||||
// 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();
|
||||
elText.value = self.model instanceof SvgTextModel ? self.model.get('text') : '——————————';
|
||||
txtColor.value = this.model.get('color');
|
||||
txtSize.value = this.model.get('fontSize');
|
||||
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
var ElementsCollection = function () {
|
||||
/**
|
||||
* @type {SvgElementModel[]}
|
||||
*/
|
||||
this.models = [];
|
||||
this.addEvent = null;
|
||||
|
||||
var lastId = 0;
|
||||
|
||||
/**
|
||||
* @param {SvgElementModel} pathModel
|
||||
*/
|
||||
this.add = function (pathModel) {
|
||||
pathModel.id = ++lastId;
|
||||
|
||||
this.models.push(pathModel);
|
||||
|
||||
if (this.addEvent) {
|
||||
this.addEvent(pathModel);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @param {number} index
|
||||
* @returns {SvgElementModel}
|
||||
*/
|
||||
this.get = function (index) {
|
||||
return this.models[index];
|
||||
};
|
||||
this.reset = function () {
|
||||
this.models.forEach(function (model) {
|
||||
model.destroy();
|
||||
})
|
||||
|
||||
this.models = [];
|
||||
};
|
||||
/**
|
||||
* @param {ElementsCollection~addEvent} callback
|
||||
*/
|
||||
this.onAdd = function (callback) {
|
||||
this.addEvent = callback;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ElementsCollection} elementsCollection
|
||||
* @param {Image} image
|
||||
* @param {number} questionId
|
||||
* @constructor
|
||||
*/
|
||||
var AnnotationCanvasView = function (elementsCollection, image, questionId) {
|
||||
var self = this;
|
||||
|
||||
this.questionId = questionId;
|
||||
this.image = image;
|
||||
|
||||
var svgImage = (function () {
|
||||
var image = document.createElementNS('http://www.w3.org/2000/svg', 'image');
|
||||
image.setAttributeNS('http://www.w3.org/1999/xlink', 'href', self.image.src);
|
||||
image.setAttribute('width', self.image.width);
|
||||
image.setAttribute('height', self.image.height);
|
||||
|
||||
return image;
|
||||
})();
|
||||
|
||||
this.el = (function () {
|
||||
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
svg.setAttribute('version', '1.1');
|
||||
svg.setAttribute('viewBox', '0 0 ' + self.image.width + ' ' + self.image.height);
|
||||
svg.setAttribute('width', self.image.width);
|
||||
svg.setAttribute('height', self.image.height);
|
||||
|
||||
return svg;
|
||||
})();
|
||||
this.el.appendChild(svgImage);
|
||||
|
||||
this.elementsCollection = elementsCollection;
|
||||
this.elementsCollection.onAdd(function (pathModel) {
|
||||
var svgElementView = null;
|
||||
|
||||
if (pathModel instanceof SvgPathModel) {
|
||||
svgElementView = new SvgPathView(pathModel);
|
||||
} else if (pathModel instanceof SvgTextModel) {
|
||||
svgElementView = new SvgTextView(pathModel);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
self.el.appendChild(svgElementView.render().el);
|
||||
|
||||
var controllerView = new ControllerView(pathModel);
|
||||
|
||||
$('#annotation-toolbar-' + self.questionId).append(controllerView.render().el);
|
||||
$(controllerView.el).children('input').eq(0).focus();
|
||||
});
|
||||
|
||||
var $rdbOptions = null;
|
||||
var $btnReset = null;
|
||||
var $btnUndo = null;
|
||||
var $btnRedo = null;
|
||||
|
||||
this.render = function () {
|
||||
$rdbOptions = $('[name="' + this.questionId + '-options"]');
|
||||
$btnReset = $('#btn-reset-' + this.questionId);
|
||||
$btnUndo = $('#btn-undo-' + this.questionId);
|
||||
$btnRedo = $('#btn-redo-' + this.questionId);
|
||||
|
||||
setEvents();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
function setEvents() {
|
||||
var isMoving = false,
|
||||
elementModel = null;
|
||||
|
||||
$(self.el)
|
||||
.on('dragstart', function (e) {
|
||||
e.preventDefault();
|
||||
})
|
||||
.on('click', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
if ("1" !== $rdbOptions.filter(':checked').val()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var point = getPointOnImage(self.el, e.clientX, e.clientY);
|
||||
elementModel = new SvgTextModel({x: point.x, y: point.y, text: ''});
|
||||
elementModel.questionId = self.questionId;
|
||||
|
||||
commandsHistory.add(new AddElementCommand(self.elementsCollection, elementModel));
|
||||
|
||||
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" !== $rdbOptions.filter(':checked').val() || elementModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
elementModel = new SvgPathModel({points: [[point.x, point.y]]});
|
||||
elementModel.questionId = self.questionId;
|
||||
self.elementsCollection.add(elementModel);
|
||||
isMoving = true;
|
||||
})
|
||||
.on('mousemove', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!isMoving || "0" !== $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" !== $rdbOptions.filter(':checked').val() || !elementModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
commandsHistory.add(new AddElementCommand(self.elementsCollection, elementModel));
|
||||
|
||||
elementModel = null;
|
||||
isMoving = false;
|
||||
});
|
||||
|
||||
$btnReset.on('click', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
commandsHistory.add(new ResetCommand(self.elementsCollection));
|
||||
|
||||
self.elementsCollection.reset();
|
||||
});
|
||||
|
||||
$btnUndo.on('click', function () {
|
||||
commandsHistory.undo();
|
||||
});
|
||||
$btnRedo.on('click', function () {
|
||||
commandsHistory.redo();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @abstract
|
||||
*/
|
||||
function Command() {}
|
||||
/**
|
||||
* @abstract
|
||||
*/
|
||||
Command.prototype.before = function () {
|
||||
throw new Error('Implement');
|
||||
}
|
||||
/**
|
||||
* @abstract
|
||||
*/
|
||||
Command.prototype.after = function () {
|
||||
throw new Error('Implement');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ElementsCollection} collection
|
||||
* @param {SvgElementModel} model
|
||||
* @constructor
|
||||
*/
|
||||
function AddElementCommand(collection, model) {
|
||||
Command.call(this);
|
||||
|
||||
this.collection = collection;
|
||||
this.model = model;
|
||||
}
|
||||
AddElementCommand.prototype = Object.create(Command.prototype);
|
||||
AddElementCommand.prototype.after = function () {
|
||||
this.collection.add(this.model);
|
||||
};
|
||||
AddElementCommand.prototype.before = function () {
|
||||
this.model.destroy();
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {SvgElementModel} model
|
||||
* @param {string} attribute
|
||||
* @param {*} newValue
|
||||
* @constructor
|
||||
* @abstract
|
||||
* @extends Command
|
||||
*/
|
||||
function ElementCommand(model, attribute, newValue) {
|
||||
Command.call(this);
|
||||
|
||||
this.model = model;
|
||||
this.attribute = attribute;
|
||||
this.oldValue = this.model.get(this.attribute);
|
||||
this.newValue = newValue;
|
||||
}
|
||||
ElementCommand.prototype = Object.create(Command.prototype);
|
||||
ElementCommand.prototype.after = function () {
|
||||
this.model.set(this.attribute, this.newValue);
|
||||
};
|
||||
ElementCommand.prototype.before = function () {
|
||||
this.model.set(this.attribute, this.oldValue);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {SvgElementModel} model
|
||||
* @param {*} newValue
|
||||
* @constructor
|
||||
* @extends ElementCommand
|
||||
*/
|
||||
function TextElementCommand(model, newValue) {
|
||||
ElementCommand.call(this, model, 'text', newValue);
|
||||
}
|
||||
TextElementCommand.prototype = Object.create(ElementCommand.prototype);
|
||||
|
||||
/**
|
||||
* @param {SvgElementModel} model
|
||||
* @param {*} newValue
|
||||
* @constructor
|
||||
* @extends ElementCommand
|
||||
*/
|
||||
function ColorElementCommand(model, newValue) {
|
||||
ElementCommand.call(this, model, 'color', newValue);
|
||||
}
|
||||
ColorElementCommand.prototype = Object.create(ElementCommand.prototype);
|
||||
|
||||
/**
|
||||
* @param {SvgElementModel} model
|
||||
* @param {*} newValue
|
||||
* @constructor
|
||||
* @extends ElementCommand
|
||||
*/
|
||||
function SizeElementCommand(model, newValue) {
|
||||
ElementCommand.call(this, model, 'fontSize', newValue);
|
||||
}
|
||||
SizeElementCommand.prototype = Object.create(ElementCommand.prototype);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {ElementsCollection} collection
|
||||
* @constructor
|
||||
* @extends Command
|
||||
*/
|
||||
function ResetCommand(collection) {
|
||||
Command.call(this);
|
||||
|
||||
this.collection = collection;
|
||||
this.oldModels = collection.models;
|
||||
}
|
||||
ResetCommand.prototype = Object.create(Command.prototype);
|
||||
ResetCommand.prototype.after = function () {
|
||||
this.collection.reset();
|
||||
};
|
||||
ResetCommand.prototype.before = function () {
|
||||
var self = this;
|
||||
|
||||
this.oldModels.forEach(function (model) {
|
||||
self.collection.add(model);
|
||||
});
|
||||
};
|
||||
|
||||
function CommandHistory() {
|
||||
var index = -1;
|
||||
/**
|
||||
* @type {Command[]}
|
||||
*/
|
||||
var commands = [];
|
||||
|
||||
/**
|
||||
* @param {Command} command
|
||||
*/
|
||||
this.add = function (command) {
|
||||
if (index > -1) {
|
||||
commands = commands.slice(0, index + 1);
|
||||
} else {
|
||||
commands = [];
|
||||
}
|
||||
|
||||
commands.push(command);
|
||||
++index;
|
||||
}
|
||||
this.undo = function () {
|
||||
(commands, index);
|
||||
if (-1 === index) {
|
||||
return;
|
||||
}
|
||||
|
||||
var command = commands[index];
|
||||
command.before();
|
||||
|
||||
--index;
|
||||
};
|
||||
this.redo = function () {
|
||||
if (index + 1 === commands.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
++index;
|
||||
|
||||
var command = commands[index];
|
||||
command.after();
|
||||
};
|
||||
};
|
||||
|
||||
var commandsHistory = new CommandHistory();
|
||||
|
||||
window.AnnotationQuestion = function (userSettings) {
|
||||
$(function () {
|
||||
var settings = $.extend(
|
||||
{
|
||||
questionId: 0,
|
||||
exerciseId: 0,
|
||||
relPath: '/'
|
||||
},
|
||||
userSettings
|
||||
),
|
||||
xhrUrl = 'exercise/annotation_user.php?' + _p.web_cid_query,
|
||||
$container = $('#annotation-canvas-' + settings.questionId);
|
||||
|
||||
$
|
||||
.getJSON(settings.relPath + xhrUrl, {
|
||||
question_id: parseInt(settings.questionId),
|
||||
exe_id: parseInt(settings.exerciseId),
|
||||
course_id: parseInt(settings.courseId)
|
||||
})
|
||||
.done(function (questionInfo) {
|
||||
var image = new Image();
|
||||
image.onload = function () {
|
||||
var elementsCollection = new ElementsCollection(),
|
||||
canvas = new AnnotationCanvasView(elementsCollection, this, parseInt(settings.questionId));
|
||||
|
||||
$container.html(canvas.render().el);
|
||||
|
||||
/** @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 = SvgTextModel.decode(textInfo);
|
||||
textModel.questionId = settings.questionId;
|
||||
elementsCollection.add(textModel);
|
||||
});
|
||||
};
|
||||
image.src = questionInfo.image.path;
|
||||
});
|
||||
});
|
||||
};
|
||||
})(window, window.jQuery);
|
||||
Reference in New Issue
Block a user