var OAAS_VIEW = (function (my, Backbone, _, $, OAAS_MODEL) {
// ================ VIEWS =======================
my.ExperimentTreeView = Backbone.View.extend({
renderTree: function (model) {
var jsonModel = model.toJSON();
var self = this;
this.tree = $('
').dynatree({
children: [jsonModel],
autoExpandMS: 750,
onActivate: function (node) {
// if we are able to remove this node from the tree, enable the remove button.
if (node && node.parent.parent != null) {
$('.remove-button', this.$el).removeAttr("disabled");
} else if (node) {
$('.remove-button', this.$el).attr("disabled", "disabled");
}
var modelNode = OAAS_MODEL.ExperimentNode.lookup(node.data.key);
if (modelNode && !modelNode.get('isExperiment')) {
$('.variate-button', this.$el).removeAttr("disabled");
} else {
$('.variate-button', this.$el).attr("disabled", "disabled");
}
},
dnd: {
onDragStart: function (node) {
return node.parent.parent != null;
},
onDragEnter: function (node, sourceNode) {
return true;
},
onDragOver: function (node, sourceNode, hitMode) {
return node.data.title !== "Experiment" || hitMode === "over";
},
onDrop: function (node, sourceNode, hitMode, ui, draggable) {
if (hitMode !== 'over')
return;
var modelNode = OAAS_MODEL.ExperimentNode.lookup(node.data.key);
if (sourceNode) {
var sourceModelNode = OAAS_MODEL.ExperimentNode.lookup(sourceNode.data.key);
sourceNode.move(node, hitMode);
// move node into the modelNode
sourceModelNode.moveNode(modelNode);
}
else {
var elem = $(draggable.element[0]);
var newNode = { nodeId: elem.attr('id'), isExperiment: elem.attr('data-isExperiment') === 'true', title: elem.attr('data-name'), key: OAAS_MODEL.ExperimentNode.nextGlobalKey() };
if (hitMode == "over") {
node.addChild(newNode);
node.expand(true);
// update the data model
newNode = modelNode.addNode(newNode);
self.trigger('node-added', newNode);
}
}
self.trigger('structure-changed', modelNode);
}
}
});
this.tree.dynatree("getRoot").visit(function (node) {
node.expand(true);
});
},
initialize: function (spec) {
this.renderTree(spec.model);
},
events: {
'click .remove-button': 'doRemove',
'click .variate-button': 'doVariate'
},
render: function () {
this.$el.empty();
this.renderTree(this.model);
var content = $(_.template($('#stepwizard_template').html(), {}));
this.tree.appendTo(this.$el);
content.appendTo(this.$el);
},
doRemove: function () {
var node = this.tree.dynatree('getActiveNode');
if (node && node.parent.parent != null) {
var nxt = node.getNextSibling();
if (nxt == null)
nxt = node.getPrevSibling();
if (nxt == null)
nxt = node.getParent();
node.remove();
OAAS_MODEL.ExperimentNode.lookup(node.data.key).remove();
if (nxt) {
this.tree.dynatree("getTree").activateKey(nxt.data.key);
}
}
this.trigger('structure-changed', node);
},
doVariate: function () {
var node = this.tree.dynatree('getActiveNode');
var modelNode = OAAS_MODEL.ExperimentNode.lookup(node.data.key);
this.trigger('variation-request', {
parentNode: node.parent,
model: modelNode
});
}
});
my.ExperimentDetailsTreeView = Backbone.View.extend({
render: function () {
var jsonModel = this.model.toJSON();
var self = this;
var tree = $('').dynatree({
children: [jsonModel],
autoExpandMS: 750,
onActivate: function (node) {
// if we click a node, simply open a dialog to enter it's parameters
self.trigger('node-clicked', node);
}
});
this.$el.empty();
tree.appendTo(this.$el);
tree.dynatree("getRoot").visit(function (node) {
node.expand(true);
});
}
});
my.DraggableGroup = Backbone.View.extend({
initialize: function () {
this.collection.bind('remove', this.render, this);
this.collection.bind('add', this.render, this);
},
render: function () {
var self = this;
var div = $('');
if (this.collection.length == 0) {
$('').text("No items found!").appendTo(div);
}
for (var i = 0; i < this.collection.length; i++) {
new my.DraggableView({ model: this.collection.models[i], el: div }).render();
}
this.$el.empty();
div.appendTo(this.$el);
}
});
my.DraggableView = Backbone.View.extend({
render: function () {
var late = _.template($("#draggable_template").html(), this.model.attributes);
$(late).draggable({
revert: true,
connectToDynatree: true,
cursorAt: { top: -5, left: -5 },
helper: "clone"
}).appendTo(this.$el);
}
});
my.SelectableGroup = Backbone.View.extend({
initialize: function () {
this.collection.bind('remove', this.render, this);
this.collection.bind('add', this.render, this);
this.collection.bind('change', this.render, this);
},
render: function () {
var self = this;
var div = $('');
if (this.collection.length == 0) {
$('').text("No experiments found!").appendTo(div);
}
for (var i = 0; i < this.collection.length; i++) {
new my.SelectableView({ model: this.collection.models[i], el: div }).render();
}
this.$el.empty();
div.selectable({
filter: 'div',
selected: function (event, ui) {
self.selected(event, ui);
}
}).appendTo(this.$el);
},
selected: function (event, ui) {
var nodeId = $(ui.selected).attr('id');
this.trigger('node-selected', nodeId);
}
});
my.SelectableView = Backbone.View.extend({
render: function () {
var late = _.template($("#draggable_template").html(), this.model.attributes);
$(late).appendTo(this.$el);
}
});
my.StepWizardView = Backbone.View.extend({
render: function () {
var self = this;
this.$el.smartWizard({
onLeaveStep: function (step) {
var stepNum = parseInt(step.attr("rel"));
return self.validateStep(stepNum);
},
onShowStep: function (step) {
var stepNum = parseInt(step.attr("rel"));
return self.showStep(stepNum);
},
onFinish: function () {
self.trigger('experiment-finished');
}
});
},
validateStep: function (stepNumber) {
var validationModel = {
stepNumber: stepNumber,
succeeded: true
};
this.trigger('step-validated', validationModel);
return validationModel.succeeded;
},
showStep: function (stepNumber) {
this.trigger('step-shown', stepNumber);
return true;
}
});
my.ParameterWizard = Backbone.View.extend({
createWizard: function (steps) {
// use template instead of jquery?!
var newWizard = $('').addClass('wizard swMain');
var stepMenu = $('
');
var prefix = name + '_';
for (var i = 0; i < steps.length; i++) {
var step = steps[i];
var a = $('', { href: '#' + prefix + (i + 1) });
$('').addClass("stepNumber").text((i + 1)).appendTo(a);
$('').addClass("stepDesc").html(step.heading + ' ' + step.description + '').appendTo(a);
var li = $('').append(a);
li.appendTo(stepMenu);
}
stepMenu.appendTo(newWizard);
for (var i = 0; i < steps.length; i++) {
var div = $('', { id: prefix + (i + 1) });
if (steps[i].content && steps[i].content != null)
div.append(steps[i].content);
div.appendTo(newWizard);
}
return newWizard;
},
render: function () {
var self = this;
this.$el.empty();
var data = this.model.get('data');
var hasProblemParameters = typeof data.ProblemParameters !== 'undefined';
var dialog = $('');
var algoDiv = $('');
var problemDiv = $('');
for (var i = 0; i < data.AlgorithmParameters.length; i++) {
var parameterView = new my.ParameterEditableView({ model: data.AlgorithmParameters[i] });
// render to dialog
parameterView.render();
parameterView.$el.appendTo(algoDiv);
}
if (hasProblemParameters) {
var problemParameters = data.ProblemParameters;
for (var i = 0; i < problemParameters.length; i++) {
var parameterView = new my.ParameterEditableView({ model: problemParameters[i] });
// render to dialog
parameterView.render();
parameterView.$el.appendTo(problemDiv);
}
}
var newWizard =
hasProblemParameters ?
this.createWizard([
{ heading: "Algorithm Parameters", description: "Adjust algorithm parameters", content: algoDiv },
{ heading: "Problem Parameters", description: "Adjust problem parameters", content: problemDiv }
]) :
this.createWizard([
{ heading: "Algorithm Parameters", description: "Adjust algorithm parameters", content: algoDiv }
]);
newWizard.appendTo(this.$el);
newWizard.smartWizard({
onFinish: function () {
self.trigger('parameters-finished');
}
});
}
});
my.ParameterEditableView = Backbone.View.extend({
/*events: {
'change': 'valueChanged'
},*/
render: function () {
var mapper = new my.DatatypeMapper();
var content = $(_.template($('#parameter_template').html(), this.model));
mapper.mapHtml(this.model, $('.rightEntry', content));
content.appendTo(this.$el);
} /*,
valueChanged: function (param) {
var value = $(param.target).val();
if (!isNaN(value))
value = parseFloat(value);
var name = $(param.target).attr("name");
// normally a controller would do this, just for once, let the view update the model
var splitted = name.split("_");
if (splitted.length == 1) {
this.model.Value = value;
} else if (splitted.length == 2) {
this.model.Value[parseInt(splitted[1])] = value;
} else if (splitted.length == 3) {
this.model.Value[parseInt(splitted[1])][parseInt(splitted[2])] = value;
}
}*/
});
my.LoadingDialog = Backbone.View.extend({
initialize: function () {
this.reset();
},
render: function () {
var self = this;
this.$el.empty();
var dialog = $('');
this.content.appendTo(dialog);
dialog.appendTo(this.$el);
this.$el.dialog({
autoOpen: true
});
},
text: function (txt) {
$('p', this.$el).text(txt);
},
setLoading: function (isLoading) {
if (isLoading)
$('img', this.$el).show();
else
$('img', this.$el).hide();
},
reset: function () {
this.content = $(_.template($('#loading_template').html(), {}));
},
setContent: function (el) {
this.content = el;
},
close: function () {
this.$el.dialog("close");
}
});
my.ParameterDialog = Backbone.View.extend({
render: function () {
var self = this;
this.$el.empty();
var data = this.model.get('data');
var hasProblemParameters = typeof data.ProblemParameters !== 'undefined';
var dialog = $('');
var parameterWizard = new my.ParameterWizard({ model: this.model, el: dialog });
parameterWizard.render();
this.listenTo(parameterWizard, 'parameters-finished', function () {
self.trigger('parameters-finished');
});
dialog.appendTo(this.$el);
this.$el.dialog({
autoOpen: true,
width: 1024,
height: 768
});
},
close: function () {
this.$el.dialog("close");
}
});
my.ExperimentDetailsView = Backbone.View.extend({
events: {
'change': 'valueChanged'
},
valueChanged: function (param) {
var value = $(param.target).val();
if (!isNaN(value))
value = parseFloat(value);
var name = $(param.target).attr("name");
if (name == 'Name')
this.model.set({ title: value });
else if (name == 'RunImmediately')
this.model.set({ run: $(param.target).is(':checked') });
else if (name == 'Repititions')
this.model.set({ repititions: value });
else if (name == 'Group')
this.model.set({ group: value });
if (this.model.get('run')) {
$('input[name="Repititions"]', this.$el).removeAttr('disabled');
$('input[name="Group"]', this.$el).removeAttr('disabled');
} else {
$('input[name="Repititions"]', this.$el).attr('disabled', 'disabled');
$('input[name="Group"]', this.$el).attr('disabled', 'disabled');
}
},
render: function () {
$('input[name="Name"]', this.$el).val(this.model.get('title'));
$('input[name="RunImmediately"]', this.$el).attr('checked', this.model.get('run'));
$('input[name="Repititions"]', this.$el).val(this.model.get('repititions'));
$('input[name="Group"]', this.$el).val(this.model.get('group'));
}
});
my.ValidationHintsView = Backbone.View.extend({
render: function () {
var template = _.template($('#validationhints_template').html(), this.model);
$(template).dialog({
resizable: false,
modal: true,
buttons: {
"OK": function () {
$(this).dialog("close");
}
}
});
}
});
// ========== Variation Dialog Views ============
my.VariationDialog = Backbone.View.extend({
initialize: function (spec) {
},
events: {
'change': 'entrySelected'
},
render: function () {
var self = this;
var dialog = $(_.template($('#variationdialog_template').html(), {}));
var content = $('select[class="variationDialogContent"]', dialog);
var data = this.model.get('data');
for (var i = 0; i < data.AlgorithmParameters.length; i++) {
data.AlgorithmParameters[i].Index = i;
var entry = new my.VariationEntryView({ model: data.AlgorithmParameters[i], el: content });
entry.render();
}
this.details = $('div[class="variationDetails"]', dialog);
this.activateButton = $('input[name="active"]', dialog);
this.activateButton.change(function (evt) {
var checked = $(evt.target).is(':checked');
if (self.selectedModel && self.selectedModel != null) {
self.selectedModel.Active = checked;
}
if (self.selectedModel.Active) {
$('*', self.details).removeAttr('disabled');
}
else {
//$('input,button,select', self.details).attr('disabled', 'disabled');
$('*', self.details).attr('disabled', 'disabled');
}
});
dialog.dialog({
height: 300,
buttons: {
"Abort": function () {
$(this).dialog("close");
},
"OK": function () {
self.generateVariations();
$(this).dialog("close");
}
}
});
content.change(function (evt) {
var optionSelected = $("option:selected", this);
var model = self.model.get('data').AlgorithmParameters[parseInt($(optionSelected).attr('data-index'))]
self.entrySelected(model);
});
this.variationDetails = new my.VariationContentView({ el: this.details });
var model = this.model.get('data').AlgorithmParameters[0];
this.entrySelected(model);
},
entrySelected: function (model) {
this.selectedModel = model;
this.variationDetails.model = model;
this.details.empty();
this.variationDetails.render();
if (model.Active) {
this.activateButton.attr('checked', 'checked');
$('*', this.details).removeAttr('disabled');
}
else {
this.activateButton.removeAttr('checked');
$('*', this.details).attr('disabled', 'disabled');
}
},
generateVariations: function () {
this.solutions = [];
this.exhaust(0, []);
this.trigger('variations-generated', this.solutions);
},
exhaust: function (index, element) {
if (index == this.model.get('data').AlgorithmParameters.length) {
// we found a solution! store it by creating a deep copy
this.solutions.push($.extend(true, [], element));
return;
}
var currentParameter = this.model.get('data').AlgorithmParameters[index];
if (currentParameter.Active && currentParameter.Generated) {
for (var i = 0; i < currentParameter.Generated.length; i++) {
element[index] = {
Name: currentParameter.Name,
Value: currentParameter.Generated[i]
};
if (currentParameter.Options) {
element[index].Options = currentParameter.Options;
}
this.exhaust(index + 1, element);
}
} else {
element[index] = {
Name: currentParameter.Name,
Value: currentParameter.Value
};
if (currentParameter.Options) {
element[index].Options = currentParameter.Options;
}
this.exhaust(index + 1, element);
}
}
});
my.TableEditView = Backbone.View.extend({
events: {
'change': 'valueChanged',
'click button[data-operation="Add"]': 'addClicked',
'click button[data-operation="Remove"]': 'removeClicked'
},
render: function () {
this.$el.empty();
// http: //stackoverflow.com/questions/8749236/create-table-with-jquery-append
var table = $('
').addClass('editableTable');
// determine dimensions
var rows = this.model.length;
var columns = 1;
if ($.isArray(this.model[0])) {
columns = this.model[0].length;
}
// create head elements
var head = $('');
var headerRow = $('
');
headerRow.appendTo(head);
if (this.options.rowNames) {
var rowNames = this.options.rowNames;
for (var i = 0; i < rowNames.length; i++) {
$('
').text(rowNames[i]).appendTo(headerRow);
}
} else {
for (var i = 0; i < columns; i++) {
$('
').text((i + 1) + '. Column').appendTo(headerRow);
}
}
head.appendTo(table);
// create body
var body = $('');
for (var i = 0; i < rows; i++) {
var row = $('
');
for (var j = 0; j < columns; j++) {
var rowContent = this.model[i];
var columnEntry = $.isArray(rowContent) ? rowContent[j] : rowContent;
var col = $('
');
if (this.options.editable) {
$('').val(columnEntry).appendTo(col);
} else {
col.text(columnEntry);
}
col.appendTo(row);
}
if (this.options.editable) {
$('').val(columnEntry).text('+').appendTo(col);
$('').val(columnEntry).text('-').appendTo(col);
}
row.appendTo(body);
}
body.appendTo(table);
table.appendTo(this.$el);
if (this.options.useDatatable)
table.dataTable();
},
valueChanged: function (evt) {
var target = $(evt.target);
var value = target.val();
var index = target.attr('name');
var splittedName = index.split('_');
var i = parseInt(splittedName[0]);
var j = parseInt(splittedName[1]);
if ($.isArray(this.model[i])) {
this.model[i][j] = parseFloat(value);
} else {
this.model[i] = parseFloat(value);
}
},
addClicked: function (evt) {
var target = $(evt.target);
var index = target.attr('name');
var i = parseInt(index);
if ($.isArray(this.model[i])) {
var cpy = [];
for (var j = 0; j < this.model[i].length; j++) {
cpy.push(this.model[i][j]);
}
this.model.splice(i, 0, cpy);
} else {
this.model.splice(i, 0, this.model[i]);
}
// render after model changes
this.render();
},
removeClicked: function (evt) {
var target = $(evt.target);
var index = target.attr('name');
var i = parseInt(index);
this.model.splice(i, 1);
// render after model changes
this.render();
}
});
my.NumberEditableView = Backbone.View.extend({
events: {
'change': 'valueChanged'
},
render: function () {
$('').val(this.model.Value).appendTo(this.$el);
},
valueChanged: function (evt) {
var value = $(evt.target).val();
if (!isNaN(value)) {
value = parseFloat(value);
this.model.Value = value;
} else {
$('input', this.$el).val(this.model.Value);
}
}
});
my.SelectionView = Backbone.View.extend({
events: {
'change': 'checked'
},
render: function () {
this.$el.empty();
var s = $('').attr('data-name', this.model.Name).appendTo(this.$el);
for (var i = 0; i < this.model.Options.length; i++) {
$('', { value: this.model.Options[i], text: this.model.Options[i] }).appendTo(s);
}
s.val(this.model.Value);
},
checked: function (evt) {
var value = $(evt.target).val();
var name = $(evt.target).attr('data-name')
this.trigger('selected', { name: name, value: value });
}
});
my.OptionSelectionView = Backbone.View.extend({
events: {
'change': 'checked'
},
render: function () {
this.$el.empty();
if ($.isArray(this.model)) {
for (var i = 0; i < this.model.length; i++) {
$(_.template($('#checkbox_template').html(), { checked: false, name: this.model[i], text: this.model[i], hideText: this.options.hideText })).appendTo(this.$el);
}
}
else {
$(_.template($('#checkbox_template').html(), { checked: this.model, name: this.model, text: this.model, hideText: this.options.hideText })).appendTo(this.$el);
}
},
checked: function (evt) {
var checked = $(evt.target).is(':checked');
var name = $(evt.target).attr('name')
this.trigger('changed', { name: name, checked: checked });
}
});
my.PlotView = Backbone.View.extend({
render: function () {
this.$el.empty();
var plotLabels = undefined;
if (this.model.RowNames) {
plotLabels = [];
for (var i = 0; i < this.model.RowNames.length; i++) {
plotLabels.push({ label: this.model.RowNames[i] });
}
}
var values = this.model.Value ? this.model.Value : this.model;
var plotValues = [];
if ($.isArray(values[0])) {
var columnCount = values[0].length;
var colVals = {};
for (var i = 0; i < columnCount; i++)
colVals[i] = [];
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < columnCount; j++) {
colVals[j].push(values[i][j]);
}
}
for (var i = 0; i < columnCount; i++) {
plotValues.push(colVals[i]);
}
} else {
plotValues.push(values);
}
if (!this.$el.attr('id')) {
this.$el.attr('id', my.PlotView.nextId());
}
this.$el.css('width', '500px');
this.plot = $.jqplot(this.$el.attr('id'), plotValues, {
cursor: {
show: true,
zoom: true,
showTooltip: false
},
series: plotLabels,
seriesDefaults: {
lineWidth: 1.5,
markerOptions: {
size: 2,
lineWidth: 2
}
},
legend: {
show: true,
placement: 'outsideGrid'
},
highlighter: {
show: true,
sizeAdjust: 7.5
}
});
},
refresh: function () {
if (this.plot)
this.plot.replot({ resetAxes: true });
}
},
{
plotId: 1,
nextId: function () {
return my.PlotView.plotId++;
}
});
my.VariationContentView = Backbone.View.extend({
render: function () {
var self = this;
if (this.model.Value === 'false' || this.model.Value === 'true' ||
this.model.Value === true || this.model.Value === false) {
$(_.template($('#variation_boolean_template').html(), {})).appendTo(this.$el);
this.model.Generated = [false, true];
}
else if (!isNaN(this.model.Value)) {
var elem = $(_.template($('#variation_number_template').html(), {}));
elem.appendTo(this.$el)
$('input[name="generate"]', elem).click(function (evt) { self.openGenerator(evt); });
if (!this.model.Generated) {
this.model.Generated = [0, 1, 2];
}
var tev = new my.TableEditView({ model: this.model.Generated, el: $('div div', this.$el), editable: true });
tev.render();
}
else if (this.model.Options) {
var osv = new my.OptionSelectionView({ model: this.model.Options, el: this.$el });
this.listenTo(osv, 'changed', function (evt) {
self.selectionChanged(evt);
});
if (!this.model.Generated)
this.model.Generated = [];
osv.render();
for (var i = 0; i < this.model.Generated.length; i++) {
$('input[name="' + this.model.Generated[i] + '"]', this.$el).attr('checked', 'checked');
}
}
},
openGenerator: function (evt) {
var self = this;
var generator = new my.GeneratorDialog();
this.listenTo(generator, 'success', function (data) {
self.doGenerate(data);
});
generator.render();
},
doGenerate: function (data) {
if (data.minimum > data.maximum) {
var tmp = data.minimum;
data.minimum = data.maximum;
data.maximum = tmp;
}
if (data.step < 0)
data.step *= -1;
if (data.step == 0)
data.step = 1;
this.model.Generated = _.range(data.minimum, data.maximum + 1, data.step);
var tev = new my.TableEditView({ model: this.model.Generated, el: $('div div', this.$el), editable: true });
tev.render();
},
selectionChanged: function (evt) {
if (!evt.checked) {
this.model.Generated = _.without(this.model.Generated, evt.name);
} else if (_.indexOf(this.model.Generated, evt.name) == -1) {
this.model.Generated.push(evt.name);
}
}
});
my.GeneratorDialog = Backbone.View.extend({
render: function () {
var self = this;
var generator = $(_.template($('#variation_generator_template').html(), {}));
generator.dialog({
buttons: {
"Abort": function () {
$(this).dialog("close");
},
"OK": function () {
$(this).dialog("close");
self.trigger("success", {
minimum: parseInt($('input[name="minimum"]', generator).val()),
maximum: parseInt($('input[name="maximum"]', generator).val()),
step: parseFloat($('input[name="step"]', generator).val())
});
}
}
});
}
});
my.VariationEntryView = Backbone.View.extend({
render: function () {
var entryTemplate = _.template($('#variationentry_template').html(), this.model);
$(entryTemplate).appendTo(this.$el);
}
});
return my;
} (OAAS_VIEW || {}, Backbone, _, $, OAAS_MODEL));