[9324] | 1 | var OAAS_VIEW = (function (my, Backbone, _, $, OAAS_MODEL) {
|
---|
| 2 | // ================ VIEWS =======================
|
---|
| 3 | my.ExperimentTreeView = Backbone.View.extend({
|
---|
| 4 | renderTree: function (model) {
|
---|
| 5 | var jsonModel = model.toJSON();
|
---|
| 6 | var self = this;
|
---|
| 7 | this.tree = $('<div />').dynatree({
|
---|
| 8 | children: [jsonModel],
|
---|
| 9 | autoExpandMS: 750,
|
---|
| 10 | onActivate: function (node) {
|
---|
| 11 | // if we are able to remove this node from the tree, enable the remove button.
|
---|
| 12 | if (node && node.parent.parent != null) {
|
---|
| 13 | $('.remove-button', this.$el).removeAttr("disabled");
|
---|
| 14 | } else if (node) {
|
---|
| 15 | $('.remove-button', this.$el).attr("disabled", "disabled");
|
---|
| 16 | }
|
---|
[9305] | 17 |
|
---|
[9324] | 18 | var modelNode = OAAS_MODEL.ExperimentNode.lookup(node.data.key);
|
---|
| 19 | if (modelNode && !modelNode.get('isExperiment')) {
|
---|
| 20 | $('.variate-button', this.$el).removeAttr("disabled");
|
---|
| 21 | } else {
|
---|
| 22 | $('.variate-button', this.$el).attr("disabled", "disabled");
|
---|
| 23 | }
|
---|
[9305] | 24 | },
|
---|
[9324] | 25 | dnd: {
|
---|
| 26 | onDragStart: function (node) {
|
---|
| 27 | return node.parent.parent != null;
|
---|
| 28 | },
|
---|
| 29 | onDragEnter: function (node, sourceNode) {
|
---|
| 30 | return true;
|
---|
| 31 | },
|
---|
| 32 | onDragOver: function (node, sourceNode, hitMode) {
|
---|
| 33 | return node.data.title !== "Experiment" || hitMode === "over";
|
---|
| 34 | },
|
---|
| 35 | onDrop: function (node, sourceNode, hitMode, ui, draggable) {
|
---|
| 36 | if (hitMode !== 'over')
|
---|
| 37 | return;
|
---|
| 38 | var modelNode = OAAS_MODEL.ExperimentNode.lookup(node.data.key);
|
---|
| 39 | if (sourceNode) {
|
---|
| 40 | var sourceModelNode = OAAS_MODEL.ExperimentNode.lookup(sourceNode.data.key);
|
---|
| 41 | sourceNode.move(node, hitMode);
|
---|
| 42 | // move node into the modelNode
|
---|
| 43 | sourceModelNode.moveNode(modelNode);
|
---|
[9305] | 44 | }
|
---|
[9324] | 45 | else {
|
---|
| 46 | var elem = $(draggable.element[0]);
|
---|
| 47 | var newNode = { nodeId: elem.attr('id'), isExperiment: elem.attr('data-isExperiment') === 'true', title: elem.attr('data-name'), key: OAAS_MODEL.ExperimentNode.nextGlobalKey() };
|
---|
| 48 | if (hitMode == "over") {
|
---|
| 49 | node.addChild(newNode);
|
---|
| 50 | node.expand(true);
|
---|
| 51 | // update the data model
|
---|
| 52 | newNode = modelNode.addNode(newNode);
|
---|
| 53 | self.trigger('node-added', newNode);
|
---|
| 54 | }
|
---|
| 55 | }
|
---|
| 56 | self.trigger('structure-changed', modelNode);
|
---|
[9305] | 57 | }
|
---|
| 58 | }
|
---|
[9324] | 59 | });
|
---|
| 60 | this.tree.dynatree("getRoot").visit(function (node) {
|
---|
| 61 | node.expand(true);
|
---|
| 62 | });
|
---|
| 63 | },
|
---|
| 64 | initialize: function (spec) {
|
---|
| 65 | this.renderTree(spec.model);
|
---|
| 66 | },
|
---|
| 67 | events: {
|
---|
| 68 | 'click .remove-button': 'doRemove',
|
---|
| 69 | 'click .variate-button': 'doVariate'
|
---|
| 70 | },
|
---|
| 71 | render: function () {
|
---|
| 72 | this.$el.empty();
|
---|
| 73 | this.renderTree(this.model);
|
---|
| 74 | var content = $(_.template($('#stepwizard_template').html(), {}));
|
---|
| 75 | this.tree.appendTo(this.$el);
|
---|
| 76 | content.appendTo(this.$el);
|
---|
| 77 | },
|
---|
| 78 | doRemove: function () {
|
---|
| 79 | var node = this.tree.dynatree('getActiveNode');
|
---|
| 80 | if (node && node.parent.parent != null) {
|
---|
| 81 | var nxt = node.getNextSibling();
|
---|
| 82 | if (nxt == null)
|
---|
| 83 | nxt = node.getPrevSibling();
|
---|
| 84 | if (nxt == null)
|
---|
| 85 | nxt = node.getParent();
|
---|
| 86 | node.remove();
|
---|
| 87 | OAAS_MODEL.ExperimentNode.lookup(node.data.key).remove();
|
---|
| 88 | if (nxt) {
|
---|
| 89 | this.tree.dynatree("getTree").activateKey(nxt.data.key);
|
---|
| 90 | }
|
---|
[9305] | 91 | }
|
---|
[9324] | 92 | this.trigger('structure-changed', node);
|
---|
| 93 | },
|
---|
| 94 | doVariate: function () {
|
---|
| 95 | var node = this.tree.dynatree('getActiveNode');
|
---|
| 96 | var modelNode = OAAS_MODEL.ExperimentNode.lookup(node.data.key);
|
---|
| 97 | this.trigger('variation-request', {
|
---|
| 98 | parentNode: node.parent,
|
---|
| 99 | model: modelNode
|
---|
| 100 | });
|
---|
[9305] | 101 | }
|
---|
[9324] | 102 | });
|
---|
[9305] | 103 |
|
---|
[9324] | 104 | my.ExperimentDetailsTreeView = Backbone.View.extend({
|
---|
| 105 | render: function () {
|
---|
| 106 | var jsonModel = this.model.toJSON();
|
---|
| 107 | var self = this;
|
---|
| 108 | var tree = $('<div />').dynatree({
|
---|
| 109 | children: [jsonModel],
|
---|
| 110 | autoExpandMS: 750,
|
---|
| 111 | onActivate: function (node) {
|
---|
| 112 | // if we click a node, simply open a dialog to enter it's parameters
|
---|
| 113 | self.trigger('node-clicked', node);
|
---|
| 114 | }
|
---|
| 115 | });
|
---|
| 116 |
|
---|
| 117 | this.$el.empty();
|
---|
| 118 | tree.appendTo(this.$el);
|
---|
| 119 | tree.dynatree("getRoot").visit(function (node) {
|
---|
| 120 | node.expand(true);
|
---|
| 121 | });
|
---|
| 122 | }
|
---|
[9305] | 123 | });
|
---|
| 124 |
|
---|
[9324] | 125 | my.DraggableGroup = Backbone.View.extend({
|
---|
| 126 | initialize: function () {
|
---|
| 127 | this.collection.bind('remove', this.render, this);
|
---|
| 128 | this.collection.bind('add', this.render, this);
|
---|
| 129 | },
|
---|
| 130 | render: function () {
|
---|
| 131 | var self = this;
|
---|
| 132 | var div = $('<div />');
|
---|
| 133 | if (this.collection.length == 0) {
|
---|
| 134 | $('<p></p>').text("No items found!").appendTo(div);
|
---|
| 135 | }
|
---|
| 136 | for (var i = 0; i < this.collection.length; i++) {
|
---|
| 137 | new my.DraggableView({ model: this.collection.models[i], el: div }).render();
|
---|
| 138 | }
|
---|
| 139 |
|
---|
| 140 | this.$el.empty();
|
---|
| 141 | div.appendTo(this.$el);
|
---|
| 142 | }
|
---|
[9305] | 143 | });
|
---|
| 144 |
|
---|
| 145 |
|
---|
[9324] | 146 | my.DraggableView = Backbone.View.extend({
|
---|
| 147 | render: function () {
|
---|
| 148 | var late = _.template($("#draggable_template").html(), this.model.attributes);
|
---|
| 149 | $(late).draggable({
|
---|
| 150 | revert: true,
|
---|
| 151 | connectToDynatree: true,
|
---|
| 152 | cursorAt: { top: -5, left: -5 },
|
---|
| 153 | helper: "clone"
|
---|
| 154 | }).appendTo(this.$el);
|
---|
[9305] | 155 | }
|
---|
[9324] | 156 | });
|
---|
[9305] | 157 |
|
---|
[9324] | 158 | my.SelectableGroup = Backbone.View.extend({
|
---|
| 159 | initialize: function () {
|
---|
| 160 | this.collection.bind('remove', this.render, this);
|
---|
| 161 | this.collection.bind('add', this.render, this);
|
---|
| 162 | this.collection.bind('change', this.render, this);
|
---|
| 163 | },
|
---|
| 164 | render: function () {
|
---|
| 165 | var self = this;
|
---|
| 166 | var div = $('<div />');
|
---|
| 167 | if (this.collection.length == 0) {
|
---|
| 168 | $('<p></p>').text("No experiments found!").appendTo(div);
|
---|
| 169 | }
|
---|
[9305] | 170 |
|
---|
[9324] | 171 | for (var i = 0; i < this.collection.length; i++) {
|
---|
| 172 | new my.SelectableView({ model: this.collection.models[i], el: div }).render();
|
---|
| 173 | }
|
---|
| 174 | this.$el.empty();
|
---|
| 175 | div.selectable({
|
---|
| 176 | filter: 'div',
|
---|
| 177 | selected: function (event, ui) {
|
---|
| 178 | self.selected(event, ui);
|
---|
| 179 | }
|
---|
| 180 | }).appendTo(this.$el);
|
---|
| 181 | },
|
---|
| 182 | selected: function (event, ui) {
|
---|
| 183 | var nodeId = $(ui.selected).attr('id');
|
---|
| 184 | this.trigger('node-selected', nodeId);
|
---|
| 185 | }
|
---|
| 186 | });
|
---|
[9305] | 187 |
|
---|
[9324] | 188 | my.SelectableView = Backbone.View.extend({
|
---|
| 189 | render: function () {
|
---|
| 190 | var late = _.template($("#draggable_template").html(), this.model.attributes);
|
---|
| 191 | $(late).appendTo(this.$el);
|
---|
| 192 | }
|
---|
| 193 | });
|
---|
[9305] | 194 |
|
---|
[9324] | 195 | my.StepWizardView = Backbone.View.extend({
|
---|
| 196 | render: function () {
|
---|
| 197 | var self = this;
|
---|
| 198 | this.$el.smartWizard({
|
---|
[9335] | 199 | keyNavigation: false,
|
---|
[9324] | 200 | onLeaveStep: function (step) {
|
---|
| 201 | var stepNum = parseInt(step.attr("rel"));
|
---|
| 202 | return self.validateStep(stepNum);
|
---|
| 203 | },
|
---|
| 204 | onShowStep: function (step) {
|
---|
| 205 | var stepNum = parseInt(step.attr("rel"));
|
---|
| 206 | return self.showStep(stepNum);
|
---|
| 207 | },
|
---|
| 208 | onFinish: function () {
|
---|
| 209 | self.trigger('experiment-finished');
|
---|
| 210 | }
|
---|
| 211 | });
|
---|
| 212 | },
|
---|
| 213 | validateStep: function (stepNumber) {
|
---|
| 214 | var validationModel = {
|
---|
| 215 | stepNumber: stepNumber,
|
---|
| 216 | succeeded: true
|
---|
| 217 | };
|
---|
| 218 | this.trigger('step-validated', validationModel);
|
---|
| 219 | return validationModel.succeeded;
|
---|
| 220 | },
|
---|
| 221 | showStep: function (stepNumber) {
|
---|
| 222 | this.trigger('step-shown', stepNumber);
|
---|
| 223 | return true;
|
---|
[9305] | 224 | }
|
---|
[9324] | 225 | });
|
---|
| 226 |
|
---|
| 227 | my.ParameterWizard = Backbone.View.extend({
|
---|
| 228 | createWizard: function (steps) {
|
---|
| 229 | // use template instead of jquery?!
|
---|
| 230 | var newWizard = $('<div />').addClass('wizard swMain');
|
---|
| 231 | var stepMenu = $('<ul />');
|
---|
| 232 | var prefix = name + '_';
|
---|
| 233 | for (var i = 0; i < steps.length; i++) {
|
---|
| 234 | var step = steps[i];
|
---|
| 235 | var a = $('<a></a>', { href: '#' + prefix + (i + 1) });
|
---|
| 236 | $('<label />').addClass("stepNumber").text((i + 1)).appendTo(a);
|
---|
| 237 | $('<span />').addClass("stepDesc").html(step.heading + '<br /><small>' + step.description + '</small>').appendTo(a);
|
---|
| 238 | var li = $('<li />').append(a);
|
---|
| 239 | li.appendTo(stepMenu);
|
---|
[9305] | 240 | }
|
---|
[9324] | 241 | stepMenu.appendTo(newWizard);
|
---|
| 242 | for (var i = 0; i < steps.length; i++) {
|
---|
| 243 | var div = $('<div />', { id: prefix + (i + 1) });
|
---|
| 244 | if (steps[i].content && steps[i].content != null)
|
---|
| 245 | div.append(steps[i].content);
|
---|
| 246 | div.appendTo(newWizard);
|
---|
| 247 | }
|
---|
| 248 | return newWizard;
|
---|
| 249 | },
|
---|
| 250 | render: function () {
|
---|
| 251 | var self = this;
|
---|
| 252 | this.$el.empty();
|
---|
| 253 | var data = this.model.get('data');
|
---|
[9305] | 254 |
|
---|
[9324] | 255 | var hasProblemParameters = typeof data.ProblemParameters !== 'undefined';
|
---|
| 256 | var dialog = $('<div />');
|
---|
| 257 | var algoDiv = $('<div />');
|
---|
| 258 | var problemDiv = $('<div />');
|
---|
[9305] | 259 |
|
---|
[9324] | 260 | for (var i = 0; i < data.AlgorithmParameters.length; i++) {
|
---|
| 261 | var parameterView = new my.ParameterEditableView({ model: data.AlgorithmParameters[i] });
|
---|
| 262 | // render to dialog
|
---|
| 263 | parameterView.render();
|
---|
| 264 | parameterView.$el.appendTo(algoDiv);
|
---|
[9305] | 265 | }
|
---|
| 266 |
|
---|
[9324] | 267 | if (hasProblemParameters) {
|
---|
| 268 | var problemParameters = data.ProblemParameters;
|
---|
| 269 | for (var i = 0; i < problemParameters.length; i++) {
|
---|
| 270 | var parameterView = new my.ParameterEditableView({ model: problemParameters[i] });
|
---|
| 271 | // render to dialog
|
---|
| 272 | parameterView.render();
|
---|
| 273 | parameterView.$el.appendTo(problemDiv);
|
---|
| 274 | }
|
---|
| 275 | }
|
---|
[9305] | 276 |
|
---|
[9324] | 277 | var newWizard =
|
---|
| 278 | hasProblemParameters ?
|
---|
| 279 | this.createWizard([
|
---|
| 280 | { heading: "Algorithm Parameters", description: "Adjust algorithm parameters", content: algoDiv },
|
---|
| 281 | { heading: "Problem Parameters", description: "Adjust problem parameters", content: problemDiv }
|
---|
| 282 | ]) :
|
---|
| 283 | this.createWizard([
|
---|
| 284 | { heading: "Algorithm Parameters", description: "Adjust algorithm parameters", content: algoDiv }
|
---|
| 285 | ]);
|
---|
[9305] | 286 |
|
---|
[9324] | 287 | newWizard.appendTo(this.$el);
|
---|
| 288 | newWizard.smartWizard({
|
---|
[9335] | 289 | keyNavigation: false,
|
---|
[9324] | 290 | onFinish: function () {
|
---|
| 291 | self.trigger('parameters-finished');
|
---|
| 292 | }
|
---|
| 293 | });
|
---|
| 294 | }
|
---|
[9305] | 295 | });
|
---|
| 296 |
|
---|
[9324] | 297 | my.ParameterEditableView = Backbone.View.extend({
|
---|
| 298 | /*events: {
|
---|
[9305] | 299 | 'change': 'valueChanged'
|
---|
[9324] | 300 | },*/
|
---|
| 301 | render: function () {
|
---|
| 302 | var mapper = new my.DatatypeMapper();
|
---|
| 303 | var content = $(_.template($('#parameter_template').html(), this.model));
|
---|
| 304 | mapper.mapHtml(this.model, $('.rightEntry', content));
|
---|
| 305 | content.appendTo(this.$el);
|
---|
| 306 | } /*,
|
---|
| 307 | valueChanged: function (param) {
|
---|
| 308 | var value = $(param.target).val();
|
---|
| 309 | if (!isNaN(value))
|
---|
| 310 | value = parseFloat(value);
|
---|
| 311 | var name = $(param.target).attr("name");
|
---|
| 312 | // normally a controller would do this, just for once, let the view update the model
|
---|
| 313 | var splitted = name.split("_");
|
---|
| 314 | if (splitted.length == 1) {
|
---|
| 315 | this.model.Value = value;
|
---|
| 316 | } else if (splitted.length == 2) {
|
---|
| 317 | this.model.Value[parseInt(splitted[1])] = value;
|
---|
| 318 | } else if (splitted.length == 3) {
|
---|
| 319 | this.model.Value[parseInt(splitted[1])][parseInt(splitted[2])] = value;
|
---|
| 320 | }
|
---|
| 321 | }*/
|
---|
| 322 | });
|
---|
[9305] | 323 |
|
---|
| 324 |
|
---|
[9324] | 325 | my.LoadingDialog = Backbone.View.extend({
|
---|
| 326 | initialize: function () {
|
---|
| 327 | this.reset();
|
---|
| 328 | },
|
---|
| 329 | render: function () {
|
---|
| 330 | var self = this;
|
---|
| 331 | this.$el.empty();
|
---|
| 332 | var dialog = $('<div />');
|
---|
| 333 | this.content.appendTo(dialog);
|
---|
| 334 | dialog.appendTo(this.$el);
|
---|
| 335 | this.$el.dialog({
|
---|
| 336 | autoOpen: true
|
---|
| 337 | });
|
---|
| 338 | },
|
---|
| 339 | text: function (txt) {
|
---|
| 340 | $('p', this.$el).text(txt);
|
---|
| 341 | },
|
---|
| 342 | setLoading: function (isLoading) {
|
---|
| 343 | if (isLoading)
|
---|
| 344 | $('img', this.$el).show();
|
---|
| 345 | else
|
---|
| 346 | $('img', this.$el).hide();
|
---|
| 347 | },
|
---|
| 348 | reset: function () {
|
---|
| 349 | this.content = $(_.template($('#loading_template').html(), {}));
|
---|
| 350 | },
|
---|
| 351 | setContent: function (el) {
|
---|
| 352 | this.content = el;
|
---|
| 353 | },
|
---|
| 354 | close: function () {
|
---|
| 355 | this.$el.dialog("close");
|
---|
| 356 | }
|
---|
| 357 | });
|
---|
[9305] | 358 |
|
---|
| 359 |
|
---|
[9324] | 360 | my.ParameterDialog = Backbone.View.extend({
|
---|
| 361 | render: function () {
|
---|
| 362 | var self = this;
|
---|
[9305] | 363 |
|
---|
[9324] | 364 | this.$el.empty();
|
---|
| 365 | var data = this.model.get('data');
|
---|
[9305] | 366 |
|
---|
[9324] | 367 | var hasProblemParameters = typeof data.ProblemParameters !== 'undefined';
|
---|
| 368 | var dialog = $('<div />');
|
---|
[9305] | 369 |
|
---|
[9324] | 370 | var parameterWizard = new my.ParameterWizard({ model: this.model, el: dialog });
|
---|
| 371 | parameterWizard.render();
|
---|
| 372 | this.listenTo(parameterWizard, 'parameters-finished', function () {
|
---|
| 373 | self.trigger('parameters-finished');
|
---|
| 374 | });
|
---|
| 375 | dialog.appendTo(this.$el);
|
---|
| 376 | this.$el.dialog({
|
---|
| 377 | autoOpen: true,
|
---|
| 378 | width: 1024,
|
---|
| 379 | height: 768
|
---|
| 380 | });
|
---|
| 381 | },
|
---|
| 382 | close: function () {
|
---|
| 383 | this.$el.dialog("close");
|
---|
| 384 | }
|
---|
[9305] | 385 | });
|
---|
| 386 |
|
---|
[9324] | 387 | my.ExperimentDetailsView = Backbone.View.extend({
|
---|
| 388 | events: {
|
---|
| 389 | 'change': 'valueChanged'
|
---|
| 390 | },
|
---|
| 391 | valueChanged: function (param) {
|
---|
| 392 | var value = $(param.target).val();
|
---|
| 393 | if (!isNaN(value))
|
---|
| 394 | value = parseFloat(value);
|
---|
| 395 | var name = $(param.target).attr("name");
|
---|
| 396 | if (name == 'Name')
|
---|
| 397 | this.model.set({ title: value });
|
---|
| 398 | else if (name == 'RunImmediately')
|
---|
| 399 | this.model.set({ run: $(param.target).is(':checked') });
|
---|
| 400 | else if (name == 'Repititions')
|
---|
| 401 | this.model.set({ repititions: value });
|
---|
| 402 | else if (name == 'Group')
|
---|
| 403 | this.model.set({ group: value });
|
---|
[9305] | 404 |
|
---|
[9324] | 405 | if (this.model.get('run')) {
|
---|
| 406 | $('input[name="Repititions"]', this.$el).removeAttr('disabled');
|
---|
| 407 | $('input[name="Group"]', this.$el).removeAttr('disabled');
|
---|
| 408 | } else {
|
---|
| 409 | $('input[name="Repititions"]', this.$el).attr('disabled', 'disabled');
|
---|
| 410 | $('input[name="Group"]', this.$el).attr('disabled', 'disabled');
|
---|
| 411 | }
|
---|
| 412 | },
|
---|
| 413 | render: function () {
|
---|
| 414 | $('input[name="Name"]', this.$el).val(this.model.get('title'));
|
---|
| 415 | $('input[name="RunImmediately"]', this.$el).attr('checked', this.model.get('run'));
|
---|
| 416 | $('input[name="Repititions"]', this.$el).val(this.model.get('repititions'));
|
---|
| 417 | $('input[name="Group"]', this.$el).val(this.model.get('group'));
|
---|
[9305] | 418 | }
|
---|
[9324] | 419 | });
|
---|
[9305] | 420 |
|
---|
[9324] | 421 | my.ValidationHintsView = Backbone.View.extend({
|
---|
| 422 | render: function () {
|
---|
| 423 | var template = _.template($('#validationhints_template').html(), this.model);
|
---|
| 424 | $(template).dialog({
|
---|
| 425 | resizable: false,
|
---|
| 426 | modal: true,
|
---|
| 427 | buttons: {
|
---|
| 428 | "OK": function () {
|
---|
| 429 | $(this).dialog("close");
|
---|
| 430 | }
|
---|
[9305] | 431 | }
|
---|
[9324] | 432 | });
|
---|
| 433 | }
|
---|
| 434 | });
|
---|
[9305] | 435 |
|
---|
[9324] | 436 | // ========== Variation Dialog Views ============
|
---|
[9305] | 437 |
|
---|
[9324] | 438 | my.VariationDialog = Backbone.View.extend({
|
---|
| 439 | initialize: function (spec) {
|
---|
[9305] | 440 |
|
---|
[9324] | 441 | },
|
---|
| 442 | events: {
|
---|
| 443 | 'change': 'entrySelected'
|
---|
| 444 | },
|
---|
| 445 | render: function () {
|
---|
| 446 | var self = this;
|
---|
| 447 | var dialog = $(_.template($('#variationdialog_template').html(), {}));
|
---|
[9305] | 448 |
|
---|
[9324] | 449 | var content = $('select[class="variationDialogContent"]', dialog);
|
---|
| 450 | var data = this.model.get('data');
|
---|
| 451 | for (var i = 0; i < data.AlgorithmParameters.length; i++) {
|
---|
| 452 | data.AlgorithmParameters[i].Index = i;
|
---|
| 453 | var entry = new my.VariationEntryView({ model: data.AlgorithmParameters[i], el: content });
|
---|
| 454 | entry.render();
|
---|
| 455 | }
|
---|
[9305] | 456 |
|
---|
[9324] | 457 | this.details = $('div[class="variationDetails"]', dialog);
|
---|
| 458 | this.activateButton = $('input[name="active"]', dialog);
|
---|
| 459 | this.activateButton.change(function (evt) {
|
---|
| 460 | var checked = $(evt.target).is(':checked');
|
---|
| 461 | if (self.selectedModel && self.selectedModel != null) {
|
---|
| 462 | self.selectedModel.Active = checked;
|
---|
[9305] | 463 | }
|
---|
| 464 |
|
---|
[9324] | 465 | if (self.selectedModel.Active) {
|
---|
| 466 | $('*', self.details).removeAttr('disabled');
|
---|
| 467 | }
|
---|
| 468 | else {
|
---|
| 469 | //$('input,button,select', self.details).attr('disabled', 'disabled');
|
---|
| 470 | $('*', self.details).attr('disabled', 'disabled');
|
---|
| 471 | }
|
---|
| 472 | });
|
---|
| 473 | dialog.dialog({
|
---|
| 474 | height: 300,
|
---|
| 475 | buttons: {
|
---|
| 476 | "Abort": function () {
|
---|
| 477 | $(this).dialog("close");
|
---|
| 478 | },
|
---|
| 479 | "OK": function () {
|
---|
| 480 | self.generateVariations();
|
---|
| 481 | $(this).dialog("close");
|
---|
| 482 | }
|
---|
| 483 | }
|
---|
| 484 | });
|
---|
[9305] | 485 |
|
---|
[9324] | 486 | content.change(function (evt) {
|
---|
| 487 | var optionSelected = $("option:selected", this);
|
---|
| 488 | var model = self.model.get('data').AlgorithmParameters[parseInt($(optionSelected).attr('data-index'))]
|
---|
| 489 | self.entrySelected(model);
|
---|
| 490 | });
|
---|
[9305] | 491 |
|
---|
[9324] | 492 | this.variationDetails = new my.VariationContentView({ el: this.details });
|
---|
| 493 | var model = this.model.get('data').AlgorithmParameters[0];
|
---|
| 494 | this.entrySelected(model);
|
---|
| 495 | },
|
---|
| 496 | entrySelected: function (model) {
|
---|
| 497 | this.selectedModel = model;
|
---|
| 498 | this.variationDetails.model = model;
|
---|
| 499 | this.details.empty();
|
---|
| 500 | this.variationDetails.render();
|
---|
| 501 | if (model.Active) {
|
---|
| 502 | this.activateButton.attr('checked', 'checked');
|
---|
| 503 | $('*', this.details).removeAttr('disabled');
|
---|
| 504 | }
|
---|
| 505 | else {
|
---|
| 506 | this.activateButton.removeAttr('checked');
|
---|
| 507 | $('*', this.details).attr('disabled', 'disabled');
|
---|
| 508 | }
|
---|
| 509 | },
|
---|
| 510 | generateVariations: function () {
|
---|
| 511 | this.solutions = [];
|
---|
| 512 | this.exhaust(0, []);
|
---|
| 513 | this.trigger('variations-generated', this.solutions);
|
---|
| 514 | },
|
---|
| 515 | exhaust: function (index, element) {
|
---|
| 516 | if (index == this.model.get('data').AlgorithmParameters.length) {
|
---|
| 517 | // we found a solution! store it by creating a deep copy
|
---|
| 518 | this.solutions.push($.extend(true, [], element));
|
---|
| 519 | return;
|
---|
| 520 | }
|
---|
| 521 |
|
---|
| 522 | var currentParameter = this.model.get('data').AlgorithmParameters[index];
|
---|
| 523 | if (currentParameter.Active && currentParameter.Generated) {
|
---|
| 524 | for (var i = 0; i < currentParameter.Generated.length; i++) {
|
---|
| 525 | element[index] = {
|
---|
| 526 | Name: currentParameter.Name,
|
---|
| 527 | Value: currentParameter.Generated[i]
|
---|
| 528 | };
|
---|
| 529 | if (currentParameter.Options) {
|
---|
| 530 | element[index].Options = currentParameter.Options;
|
---|
| 531 | }
|
---|
| 532 | this.exhaust(index + 1, element);
|
---|
| 533 | }
|
---|
| 534 | } else {
|
---|
[9305] | 535 | element[index] = {
|
---|
| 536 | Name: currentParameter.Name,
|
---|
[9324] | 537 | Value: currentParameter.Value
|
---|
[9305] | 538 | };
|
---|
| 539 | if (currentParameter.Options) {
|
---|
| 540 | element[index].Options = currentParameter.Options;
|
---|
| 541 | }
|
---|
| 542 | this.exhaust(index + 1, element);
|
---|
| 543 | }
|
---|
| 544 | }
|
---|
[9324] | 545 | });
|
---|
[9305] | 546 |
|
---|
[9324] | 547 | my.TableEditView = Backbone.View.extend({
|
---|
| 548 | events: {
|
---|
| 549 | 'change': 'valueChanged',
|
---|
| 550 | 'click button[data-operation="Add"]': 'addClicked',
|
---|
| 551 | 'click button[data-operation="Remove"]': 'removeClicked'
|
---|
| 552 | },
|
---|
| 553 | render: function () {
|
---|
| 554 | this.$el.empty();
|
---|
| 555 | // http: //stackoverflow.com/questions/8749236/create-table-with-jquery-append
|
---|
| 556 | var table = $('<table></table>').addClass('editableTable');
|
---|
[9305] | 557 |
|
---|
[9324] | 558 | // determine dimensions
|
---|
| 559 | var rows = this.model.length;
|
---|
| 560 | var columns = 1;
|
---|
| 561 | if ($.isArray(this.model[0])) {
|
---|
| 562 | columns = this.model[0].length;
|
---|
| 563 | }
|
---|
[9305] | 564 |
|
---|
[9324] | 565 | // create head elements
|
---|
| 566 | var head = $('<thead></thead>');
|
---|
| 567 | var headerRow = $('<tr></tr>');
|
---|
| 568 | headerRow.appendTo(head);
|
---|
| 569 | if (this.options.rowNames) {
|
---|
| 570 | var rowNames = this.options.rowNames;
|
---|
| 571 | for (var i = 0; i < rowNames.length; i++) {
|
---|
| 572 | $('<td />').text(rowNames[i]).appendTo(headerRow);
|
---|
| 573 | }
|
---|
| 574 | } else {
|
---|
| 575 | for (var i = 0; i < columns; i++) {
|
---|
| 576 | $('<td />').text((i + 1) + '. Column').appendTo(headerRow);
|
---|
| 577 | }
|
---|
| 578 | }
|
---|
| 579 | head.appendTo(table);
|
---|
[9305] | 580 |
|
---|
[9324] | 581 | // create body
|
---|
| 582 | var body = $('<tbody></tbody>');
|
---|
[9305] | 583 |
|
---|
[9324] | 584 | for (var i = 0; i < rows; i++) {
|
---|
| 585 | var row = $('<tr></tr>');
|
---|
| 586 | for (var j = 0; j < columns; j++) {
|
---|
| 587 | var rowContent = this.model[i];
|
---|
| 588 | var columnEntry = $.isArray(rowContent) ? rowContent[j] : rowContent;
|
---|
| 589 | var col = $('<td></td>');
|
---|
| 590 | if (this.options.editable) {
|
---|
| 591 | $('<input type="text" name="' + i + '_' + j + '" />').val(columnEntry).appendTo(col);
|
---|
| 592 | } else {
|
---|
| 593 | col.text(columnEntry);
|
---|
| 594 | }
|
---|
| 595 | col.appendTo(row);
|
---|
| 596 | }
|
---|
| 597 | if (this.options.editable) {
|
---|
| 598 | $('<button data-operation="Add" name="' + i + '" />').val(columnEntry).text('+').appendTo(col);
|
---|
| 599 | $('<button data-operation="Remove" name="' + i + '" />').val(columnEntry).text('-').appendTo(col);
|
---|
| 600 | }
|
---|
| 601 | row.appendTo(body);
|
---|
[9305] | 602 | }
|
---|
[9324] | 603 | body.appendTo(table);
|
---|
| 604 |
|
---|
| 605 | table.appendTo(this.$el);
|
---|
| 606 | if (this.options.useDatatable)
|
---|
| 607 | table.dataTable();
|
---|
| 608 | },
|
---|
| 609 | valueChanged: function (evt) {
|
---|
| 610 | var target = $(evt.target);
|
---|
| 611 | var value = target.val();
|
---|
| 612 | var index = target.attr('name');
|
---|
| 613 | var splittedName = index.split('_');
|
---|
| 614 | var i = parseInt(splittedName[0]);
|
---|
| 615 | var j = parseInt(splittedName[1]);
|
---|
| 616 | if ($.isArray(this.model[i])) {
|
---|
| 617 | this.model[i][j] = parseFloat(value);
|
---|
| 618 | } else {
|
---|
| 619 | this.model[i] = parseFloat(value);
|
---|
[9305] | 620 | }
|
---|
[9324] | 621 | },
|
---|
| 622 | addClicked: function (evt) {
|
---|
| 623 | var target = $(evt.target);
|
---|
| 624 | var index = target.attr('name');
|
---|
| 625 | var i = parseInt(index);
|
---|
| 626 | if ($.isArray(this.model[i])) {
|
---|
| 627 | var cpy = [];
|
---|
| 628 | for (var j = 0; j < this.model[i].length; j++) {
|
---|
| 629 | cpy.push(this.model[i][j]);
|
---|
| 630 | }
|
---|
| 631 | this.model.splice(i, 0, cpy);
|
---|
| 632 | } else {
|
---|
| 633 | this.model.splice(i, 0, this.model[i]);
|
---|
[9305] | 634 | }
|
---|
[9324] | 635 | // render after model changes
|
---|
| 636 | this.render();
|
---|
| 637 | },
|
---|
| 638 | removeClicked: function (evt) {
|
---|
| 639 | var target = $(evt.target);
|
---|
| 640 | var index = target.attr('name');
|
---|
| 641 | var i = parseInt(index);
|
---|
| 642 | this.model.splice(i, 1);
|
---|
| 643 | // render after model changes
|
---|
| 644 | this.render();
|
---|
[9305] | 645 | }
|
---|
[9324] | 646 | });
|
---|
[9305] | 647 |
|
---|
[9324] | 648 | my.NumberEditableView = Backbone.View.extend({
|
---|
| 649 | events: {
|
---|
| 650 | 'change': 'valueChanged'
|
---|
| 651 | },
|
---|
| 652 | render: function () {
|
---|
| 653 | $('<input type="text" />').val(this.model.Value).appendTo(this.$el);
|
---|
[9305] | 654 |
|
---|
[9324] | 655 | },
|
---|
| 656 | valueChanged: function (evt) {
|
---|
| 657 | var value = $(evt.target).val();
|
---|
| 658 | if (!isNaN(value)) {
|
---|
| 659 | value = parseFloat(value);
|
---|
| 660 | this.model.Value = value;
|
---|
| 661 | } else {
|
---|
| 662 | $('input', this.$el).val(this.model.Value);
|
---|
| 663 | }
|
---|
[9305] | 664 | }
|
---|
[9324] | 665 | });
|
---|
[9305] | 666 |
|
---|
[9324] | 667 | my.SelectionView = Backbone.View.extend({
|
---|
| 668 | events: {
|
---|
| 669 | 'change': 'checked'
|
---|
| 670 | },
|
---|
| 671 | render: function () {
|
---|
| 672 | this.$el.empty();
|
---|
| 673 | var s = $('<select />').attr('data-name', this.model.Name).appendTo(this.$el);
|
---|
| 674 | for (var i = 0; i < this.model.Options.length; i++) {
|
---|
| 675 | $('<option />', { value: this.model.Options[i], text: this.model.Options[i] }).appendTo(s);
|
---|
| 676 | }
|
---|
| 677 | s.val(this.model.Value);
|
---|
| 678 | },
|
---|
| 679 | checked: function (evt) {
|
---|
| 680 | var value = $(evt.target).val();
|
---|
| 681 | var name = $(evt.target).attr('data-name')
|
---|
| 682 | this.trigger('selected', { name: name, value: value });
|
---|
[9305] | 683 | }
|
---|
[9324] | 684 | });
|
---|
| 685 |
|
---|
| 686 | my.OptionSelectionView = Backbone.View.extend({
|
---|
| 687 | events: {
|
---|
| 688 | 'change': 'checked'
|
---|
| 689 | },
|
---|
| 690 | render: function () {
|
---|
| 691 | this.$el.empty();
|
---|
| 692 | if ($.isArray(this.model)) {
|
---|
| 693 | for (var i = 0; i < this.model.length; i++) {
|
---|
| 694 | $(_.template($('#checkbox_template').html(), { checked: false, name: this.model[i], text: this.model[i], hideText: this.options.hideText })).appendTo(this.$el);
|
---|
| 695 | }
|
---|
[9305] | 696 | }
|
---|
[9324] | 697 | else {
|
---|
| 698 | $(_.template($('#checkbox_template').html(), { checked: this.model, name: this.model, text: this.model, hideText: this.options.hideText })).appendTo(this.$el);
|
---|
| 699 | }
|
---|
| 700 | },
|
---|
| 701 | checked: function (evt) {
|
---|
| 702 | var checked = $(evt.target).is(':checked');
|
---|
| 703 | var name = $(evt.target).attr('name')
|
---|
| 704 | this.trigger('changed', { name: name, checked: checked });
|
---|
[9305] | 705 | }
|
---|
[9324] | 706 | });
|
---|
| 707 |
|
---|
| 708 | my.VariationContentView = Backbone.View.extend({
|
---|
| 709 | render: function () {
|
---|
| 710 | var self = this;
|
---|
| 711 | if (this.model.Value === 'false' || this.model.Value === 'true' ||
|
---|
| 712 | this.model.Value === true || this.model.Value === false) {
|
---|
| 713 | $(_.template($('#variation_boolean_template').html(), {})).appendTo(this.$el);
|
---|
| 714 | this.model.Generated = [false, true];
|
---|
| 715 | }
|
---|
| 716 | else if (!isNaN(this.model.Value)) {
|
---|
| 717 | var elem = $(_.template($('#variation_number_template').html(), {}));
|
---|
| 718 | elem.appendTo(this.$el)
|
---|
| 719 | $('input[name="generate"]', elem).click(function (evt) { self.openGenerator(evt); });
|
---|
| 720 | if (!this.model.Generated) {
|
---|
| 721 | this.model.Generated = [0, 1, 2];
|
---|
[9305] | 722 | }
|
---|
[9324] | 723 | var tev = new my.TableEditView({ model: this.model.Generated, el: $('div div', this.$el), editable: true });
|
---|
| 724 | tev.render();
|
---|
[9305] | 725 | }
|
---|
[9324] | 726 | else if (this.model.Options) {
|
---|
| 727 | var osv = new my.OptionSelectionView({ model: this.model.Options, el: this.$el });
|
---|
| 728 | this.listenTo(osv, 'changed', function (evt) {
|
---|
| 729 | self.selectionChanged(evt);
|
---|
| 730 | });
|
---|
| 731 | if (!this.model.Generated)
|
---|
| 732 | this.model.Generated = [];
|
---|
| 733 | osv.render();
|
---|
| 734 | for (var i = 0; i < this.model.Generated.length; i++) {
|
---|
| 735 | $('input[name="' + this.model.Generated[i] + '"]', this.$el).attr('checked', 'checked');
|
---|
| 736 | }
|
---|
| 737 | }
|
---|
| 738 | },
|
---|
| 739 | openGenerator: function (evt) {
|
---|
| 740 | var self = this;
|
---|
| 741 | var generator = new my.GeneratorDialog();
|
---|
| 742 | this.listenTo(generator, 'success', function (data) {
|
---|
| 743 | self.doGenerate(data);
|
---|
| 744 | });
|
---|
| 745 | generator.render();
|
---|
| 746 | },
|
---|
| 747 | doGenerate: function (data) {
|
---|
| 748 | if (data.minimum > data.maximum) {
|
---|
| 749 | var tmp = data.minimum;
|
---|
| 750 | data.minimum = data.maximum;
|
---|
| 751 | data.maximum = tmp;
|
---|
| 752 | }
|
---|
| 753 | if (data.step < 0)
|
---|
| 754 | data.step *= -1;
|
---|
| 755 | if (data.step == 0)
|
---|
| 756 | data.step = 1;
|
---|
| 757 | this.model.Generated = _.range(data.minimum, data.maximum + 1, data.step);
|
---|
| 758 | var tev = new my.TableEditView({ model: this.model.Generated, el: $('div div', this.$el), editable: true });
|
---|
| 759 | tev.render();
|
---|
| 760 | },
|
---|
| 761 | selectionChanged: function (evt) {
|
---|
| 762 | if (!evt.checked) {
|
---|
| 763 | this.model.Generated = _.without(this.model.Generated, evt.name);
|
---|
| 764 | } else if (_.indexOf(this.model.Generated, evt.name) == -1) {
|
---|
| 765 | this.model.Generated.push(evt.name);
|
---|
| 766 | }
|
---|
| 767 | }
|
---|
| 768 | });
|
---|
[9305] | 769 |
|
---|
[9324] | 770 | my.GeneratorDialog = Backbone.View.extend({
|
---|
| 771 | render: function () {
|
---|
| 772 | var self = this;
|
---|
| 773 | var generator = $(_.template($('#variation_generator_template').html(), {}));
|
---|
| 774 | generator.dialog({
|
---|
| 775 | buttons: {
|
---|
| 776 | "Abort": function () {
|
---|
| 777 | $(this).dialog("close");
|
---|
| 778 | },
|
---|
| 779 | "OK": function () {
|
---|
| 780 | $(this).dialog("close");
|
---|
| 781 | self.trigger("success", {
|
---|
| 782 | minimum: parseInt($('input[name="minimum"]', generator).val()),
|
---|
| 783 | maximum: parseInt($('input[name="maximum"]', generator).val()),
|
---|
| 784 | step: parseFloat($('input[name="step"]', generator).val())
|
---|
| 785 | });
|
---|
| 786 | }
|
---|
| 787 | }
|
---|
| 788 | });
|
---|
| 789 | }
|
---|
| 790 | });
|
---|
| 791 |
|
---|
| 792 | my.VariationEntryView = Backbone.View.extend({
|
---|
| 793 | render: function () {
|
---|
| 794 | var entryTemplate = _.template($('#variationentry_template').html(), this.model);
|
---|
| 795 | $(entryTemplate).appendTo(this.$el);
|
---|
| 796 | }
|
---|
| 797 | });
|
---|
[9335] | 798 |
|
---|
| 799 | // ========================== Run Collection Views ===================================
|
---|
| 800 |
|
---|
| 801 | function percentile(p, data) {
|
---|
| 802 | var i = (data.length * p) / 100 + 0.5;
|
---|
| 803 | var f = i - parseInt(i);
|
---|
| 804 | var k = Math.floor(i) - 1;
|
---|
| 805 | return (1 - f) * data[k] + f * data[k + 1];
|
---|
| 806 | }
|
---|
| 807 |
|
---|
| 808 | my.RunCollectionView = Backbone.View.extend({
|
---|
| 809 | events: {
|
---|
| 810 | 'change .x': 'xAxisSelected',
|
---|
| 811 | 'change .y': 'yAxisSelected',
|
---|
| 812 | 'change .plotType': 'plotSelected',
|
---|
| 813 | 'change .datatableOptions': 'datatableSelected',
|
---|
| 814 | 'change .datatableRow': 'datatableRowSelected',
|
---|
| 815 | 'change .bubbleSize': 'bubbleTypeSelected'
|
---|
| 816 | },
|
---|
| 817 | render: function () {
|
---|
[9508] | 818 | if (this.model.models.length == 0)
|
---|
| 819 | return;
|
---|
| 820 |
|
---|
[9335] | 821 | var self = this;
|
---|
| 822 | var ele = $(_.template($('#runcollection_template').html(), {}));
|
---|
| 823 | var xAxisSelect = $('.x', ele);
|
---|
| 824 | var yAxisSelect = $('.y', ele);
|
---|
| 825 | var results = this.model.models[0].get('results');
|
---|
| 826 | this.options.datatables = {};
|
---|
| 827 | var datatableSelect = $('.datatableOptions', ele);
|
---|
| 828 |
|
---|
[9350] | 829 | var names = [];
|
---|
| 830 | var previouslyFound = { results: {}, params: {} };
|
---|
| 831 | _.each(this.model.models, function (model) {
|
---|
| 832 | _.each(model.get('results'), function (res) {
|
---|
| 833 | if (!previouslyFound['results'][res.Name]) {
|
---|
| 834 | previouslyFound['results'][res.Name] = true;
|
---|
| 835 | names.push({ name: res.Name, source: 'results' });
|
---|
| 836 | }
|
---|
| 837 | });
|
---|
| 838 | _.each(model.get('params'), function (res) {
|
---|
| 839 | if (!previouslyFound['params'][res.Name]) {
|
---|
| 840 | previouslyFound['params'][res.Name] = true;
|
---|
| 841 | names.push({ name: res.Name, source: 'params' });
|
---|
| 842 | }
|
---|
| 843 | });
|
---|
| 844 | });
|
---|
| 845 | previouslyFound = {};
|
---|
| 846 | this.options.modelMapping = names;
|
---|
[9335] | 847 | this.bubbleSelect = $('select[class="bubbleSize"]', ele);
|
---|
| 848 |
|
---|
[9350] | 849 | _.each(names, function (entry) {
|
---|
| 850 | var option = $("<option />", { value: entry.name, text: entry.name });
|
---|
[9335] | 851 | option.clone().appendTo(xAxisSelect);
|
---|
| 852 | option.clone().appendTo(yAxisSelect);
|
---|
| 853 | option.clone().appendTo(self.bubbleSelect);
|
---|
| 854 | var rowNames = null;
|
---|
| 855 | var foundModel = _.find(self.model.models, function (model) {
|
---|
[9350] | 856 | var e = _.find(model.get(entry.source), function (e) { return e.Name == entry.name && e.RowNames });
|
---|
| 857 | if (e)
|
---|
| 858 | rowNames = e.RowNames;
|
---|
| 859 | return e && e.RowNames;
|
---|
[9335] | 860 | });
|
---|
| 861 | if (rowNames) {
|
---|
[9350] | 862 | self.options.datatables[entry.name] = rowNames;
|
---|
| 863 | $('<option />', { value: entry.name, text: entry.name }).appendTo(datatableSelect);
|
---|
[9335] | 864 | }
|
---|
| 865 | });
|
---|
| 866 |
|
---|
| 867 | // prepare slider settings
|
---|
| 868 | this.options.bubbleSize = 10;
|
---|
| 869 | this.options.xAxis = this.model.models[0].get('results')[0].Name;
|
---|
| 870 | this.options.yAxis = this.options.xAxis;
|
---|
[9350] | 871 | this.options.selectedXIndex = 0;
|
---|
| 872 | this.options.selectedYIndex = 0;
|
---|
[9335] | 873 | this.options.plot = 'Boxplot';
|
---|
| 874 | $('div[class="bubbleSlider"]', ele).slider({
|
---|
| 875 | range: "max",
|
---|
| 876 | min: 10,
|
---|
| 877 | max: 40,
|
---|
| 878 | value: 10,
|
---|
| 879 | stop: function (event, ui) {
|
---|
| 880 | self.options.bubbleSize = ui.value;
|
---|
| 881 | self.createPlot();
|
---|
| 882 | }
|
---|
| 883 | }).css('width', '120px').css('margin-left', '15px').css('margin-right', '15px').css('display', 'inline-block');
|
---|
| 884 | ele.appendTo(this.$el);
|
---|
| 885 |
|
---|
| 886 | // init resizer
|
---|
| 887 | this.plotWidth = 500;
|
---|
| 888 | this.plotHeight = 500;
|
---|
| 889 | var parent = $('div[class="resizer"]', this.$el);
|
---|
| 890 | this.resizerView = new my.ResizableView({ el: parent });
|
---|
| 891 | this.listenTo(this.resizerView, 'resized', function (evt) {
|
---|
| 892 | self.updatePlot(
|
---|
| 893 | $(evt.target).width() * 0.96,
|
---|
| 894 | $(evt.target).height() * 0.96
|
---|
| 895 | );
|
---|
| 896 | });
|
---|
| 897 | this.resizerView.render();
|
---|
| 898 | this.updateRowSelect(datatableSelect.val());
|
---|
| 899 | this.createPlot();
|
---|
| 900 | this.updatePlot(
|
---|
| 901 | 600,
|
---|
| 902 | 500
|
---|
| 903 | );
|
---|
| 904 | },
|
---|
| 905 | updateRowSelect: function (name) {
|
---|
| 906 | if (!name)
|
---|
| 907 | return;
|
---|
| 908 | var rowSelect = $('select[class="datatableRow"]', this.$el);
|
---|
| 909 | rowSelect.empty();
|
---|
| 910 | var rownames = this.options.datatables[name];
|
---|
| 911 | for (var i = 0; i < rownames.length; i++) {
|
---|
| 912 | $('<option />', { value: rownames[i], text: rownames[i] }).appendTo(rowSelect);
|
---|
| 913 | }
|
---|
| 914 | this.options.selectedDatatable = name;
|
---|
| 915 | this.options.datatableRow = rowSelect.val();
|
---|
| 916 | this.createPlot();
|
---|
| 917 | },
|
---|
| 918 | datatableSelected: function (evt) {
|
---|
| 919 | var value = $(evt.target).val();
|
---|
| 920 | this.options.selectedDatatable = value;
|
---|
| 921 | this.updateRowSelect(value);
|
---|
| 922 | },
|
---|
| 923 | datatableRowSelected: function (evt) {
|
---|
| 924 | var value = $(evt.target).val();
|
---|
| 925 | this.options.datatableRow = value;
|
---|
| 926 | this.createPlot();
|
---|
| 927 | },
|
---|
| 928 | xAxisSelected: function (evt) {
|
---|
| 929 | var target = $(evt.target);
|
---|
[9350] | 930 | this.options.selectedXIndex = target.prop("selectedIndex");
|
---|
[9335] | 931 | this.options.xAxis = target.val();
|
---|
| 932 | this.createPlot();
|
---|
| 933 | },
|
---|
| 934 | yAxisSelected: function (evt) {
|
---|
| 935 | var target = $(evt.target);
|
---|
[9350] | 936 | this.options.selectedYIndex = target.prop("selectedIndex");
|
---|
[9335] | 937 | this.options.yAxis = target.val();
|
---|
| 938 | this.createPlot();
|
---|
| 939 | },
|
---|
| 940 | plotSelected: function (evt) {
|
---|
| 941 | var target = $(evt.target);
|
---|
| 942 | this.options.plot = target.val();
|
---|
| 943 | this.createPlot();
|
---|
| 944 | },
|
---|
| 945 | bubbleTypeSelected: function (evt) {
|
---|
| 946 | this.createPlot();
|
---|
| 947 | },
|
---|
| 948 | updateVisibility: function (evt) {
|
---|
| 949 | if (this.options.plot == 'Boxplot') {
|
---|
| 950 | $('span[class="choice"]', this.$el).show();
|
---|
| 951 | $('span[class="bubble"]', this.$el).hide();
|
---|
| 952 | $('span[class="datatable"]', this.$el).hide();
|
---|
| 953 | } else if (this.options.plot == 'Bubblechart') {
|
---|
| 954 | $('span[class="choice"]', this.$el).show();
|
---|
| 955 | $('span[class="bubble"]', this.$el).show();
|
---|
| 956 | $('span[class="datatable"]', this.$el).hide();
|
---|
| 957 | } else if (this.options.plot == 'Datatable') {
|
---|
| 958 | $('span[class="choice"]', this.$el).hide();
|
---|
| 959 | $('span[class="bubble"]', this.$el).hide();
|
---|
| 960 | $('span[class="datatable"]', this.$el).show();
|
---|
| 961 | }
|
---|
| 962 | },
|
---|
| 963 | updatePlot: function (width, height) {
|
---|
| 964 | this.plotDiv.height(height);
|
---|
| 965 | this.plotDiv.width(width);
|
---|
| 966 | this.plotWidth = width;
|
---|
| 967 | this.plotHeight = height;
|
---|
| 968 | this.plot.refresh();
|
---|
| 969 | },
|
---|
| 970 | createPlot: function () {
|
---|
| 971 | if (this.options.plot && this.options.xAxis && this.options.yAxis) {
|
---|
| 972 | if (this.options.plot == 'Boxplot') {
|
---|
| 973 | this.createBoxplot();
|
---|
| 974 | } else if (this.options.plot == 'Bubblechart') {
|
---|
| 975 | this.createBubblechart();
|
---|
| 976 | } else if (this.options.plot == 'Datatable') {
|
---|
| 977 | this.createDatatable();
|
---|
| 978 | }
|
---|
| 979 | this.updateVisibility();
|
---|
| 980 | }
|
---|
| 981 | },
|
---|
| 982 | preparePlotDiv: function () {
|
---|
| 983 | var parent = $('div[class="plot"]', this.$el);
|
---|
| 984 | parent.empty();
|
---|
| 985 | this.plotDiv = $('<div></div>').css('width', this.plotWidth).css('height', this.plotHeight).appendTo(parent);
|
---|
| 986 | return this.plotDiv;
|
---|
| 987 | },
|
---|
| 988 | createBoxplot: function () {
|
---|
| 989 | var self = this;
|
---|
[9350] | 990 | var parameterXSource = this.options.modelMapping[this.options.selectedXIndex].source; // either 'params' or 'results'
|
---|
| 991 | var parameterYSource = this.options.modelMapping[this.options.selectedYIndex].source; // either 'params' or 'results'
|
---|
[9335] | 992 | // prepare data for boxplot
|
---|
| 993 | var values = {};
|
---|
| 994 | for (var i = 0; i < this.model.models.length; i++) {
|
---|
[9350] | 995 | var xlabel = _.find(this.model.models[i].get(parameterXSource), function (itm) { return itm.Name == self.options.xAxis });
|
---|
[9335] | 996 | if (!xlabel)
|
---|
| 997 | continue;
|
---|
| 998 |
|
---|
| 999 | if ($.isArray(xlabel.Value))
|
---|
[9350] | 1000 | values[parameterXSource + '.' + xlabel.Name + ' = ' + xlabel.Value[0]] = [];
|
---|
[9335] | 1001 | else
|
---|
[9350] | 1002 | values[parameterXSource + '.' + xlabel.Name + ' = ' + xlabel.Value] = [];
|
---|
[9335] | 1003 | }
|
---|
| 1004 |
|
---|
| 1005 | for (var i = 0; i < this.model.models.length; i++) {
|
---|
[9350] | 1006 | var xlabel = _.find(this.model.models[i].get(parameterXSource), function (itm) { return itm.Name == self.options.xAxis });
|
---|
| 1007 | var entry = _.find(this.model.models[i].get(parameterYSource), function (itm) { return itm.Name == self.options.yAxis });
|
---|
[9335] | 1008 | if (!xlabel || !entry || !entry.Value)
|
---|
| 1009 | continue;
|
---|
| 1010 |
|
---|
[9350] | 1011 | var index = parameterXSource + '.' + xlabel.Name + ' = ' + ($.isArray(xlabel.Value) ? xlabel.Value[0] : xlabel.Value);
|
---|
[9335] | 1012 |
|
---|
| 1013 | if ($.isArray(entry.Value))
|
---|
| 1014 | values[index].push(entry.Value[0]);
|
---|
| 1015 | else
|
---|
| 1016 | values[index].push(entry.Value);
|
---|
| 1017 | }
|
---|
| 1018 |
|
---|
| 1019 | // create boxplot
|
---|
| 1020 | var div = this.preparePlotDiv();
|
---|
| 1021 | var boxPlot = new my.BoxplotView({ model: values, el: div });
|
---|
| 1022 | this.plot = boxPlot;
|
---|
| 1023 | boxPlot.render();
|
---|
| 1024 | },
|
---|
| 1025 | createBubblechart: function () {
|
---|
| 1026 | var self = this;
|
---|
[9350] | 1027 | var parameterXSource = this.options.modelMapping[this.options.selectedXIndex].source; // either 'params' or 'results'
|
---|
| 1028 | var parameterYSource = this.options.modelMapping[this.options.selectedYIndex].source; // either 'params' or 'results'
|
---|
[9335] | 1029 | // prepare data for bubble chart
|
---|
| 1030 | var values = [];
|
---|
| 1031 | var autoscaleBubbles = this.bubbleSelect.val() != 'Constant';
|
---|
| 1032 | for (var i = 0; i < this.model.models.length; i++) {
|
---|
[9350] | 1033 | var xValue = _.find(this.model.models[i].get(parameterXSource), function (itm) { return itm.Name == self.options.xAxis });
|
---|
| 1034 | var yValue = _.find(this.model.models[i].get(parameterYSource), function (itm) { return itm.Name == self.options.yAxis });
|
---|
[9335] | 1035 | if (!xValue || !yValue)
|
---|
| 1036 | continue;
|
---|
| 1037 |
|
---|
| 1038 | // determine size
|
---|
| 1039 | var size = 0;
|
---|
| 1040 | if (this.bubbleSelect.val() == 'Constant') {
|
---|
| 1041 | size = this.options.bubbleSize;
|
---|
| 1042 | } else {
|
---|
| 1043 | var valueProvider = _.find(this.model.models[i].get('results'), function (itm) { return itm.Name == self.bubbleSelect.val(); });
|
---|
[9508] | 1044 | if (!valueProvider)
|
---|
[9350] | 1045 | valueProvider = _.find(this.model.models[i].get('params'), function (itm) { return itm.Name == self.bubbleSelect.val(); });
|
---|
| 1046 |
|
---|
[9508] | 1047 | if (!valueProvider) {
|
---|
[9335] | 1048 | size = this.options.bubbleSize;
|
---|
| 1049 | } else {
|
---|
| 1050 | var value = valueProvider.Value;
|
---|
| 1051 | while ($.isArray(value)) {
|
---|
| 1052 | value = value[0];
|
---|
| 1053 | }
|
---|
| 1054 | size = parseInt(value.replace(/,/, '.'));
|
---|
| 1055 | if (isNaN(size))
|
---|
| 1056 | size = this.options.bubbleSize;
|
---|
| 1057 | }
|
---|
| 1058 | }
|
---|
| 1059 |
|
---|
| 1060 | // add element
|
---|
| 1061 | var xFloat = parseFloat(xValue.Value.replace(/,/, '.'));
|
---|
| 1062 | var yFloat = parseFloat(yValue.Value.replace(/,/, '.'));
|
---|
| 1063 |
|
---|
| 1064 | values.push([xFloat, yFloat, size, 'Run' + (i + 1)]);
|
---|
| 1065 | }
|
---|
| 1066 |
|
---|
| 1067 | // render bubble chart
|
---|
| 1068 | var div = this.preparePlotDiv();
|
---|
| 1069 | var bubblePlot = new my.BubbleplotView({ model: values, el: div, autoscale: autoscaleBubbles });
|
---|
| 1070 | this.plot = bubblePlot;
|
---|
| 1071 | bubblePlot.render();
|
---|
| 1072 | },
|
---|
| 1073 | createDatatable: function () {
|
---|
| 1074 | if (!this.options.selectedDatatable)
|
---|
| 1075 | return;
|
---|
| 1076 | var self = this;
|
---|
| 1077 |
|
---|
| 1078 | // prepare data for datatable
|
---|
| 1079 | var values = [];
|
---|
| 1080 | var rowNames = [];
|
---|
| 1081 | for (var i = 0; i < this.model.models.length; i++) {
|
---|
| 1082 | var table = _.find(this.model.models[i].get('results'), function (itm) { return itm.Name == self.options.selectedDatatable });
|
---|
| 1083 | if (!table)
|
---|
[9350] | 1084 | table = _.find(this.model.models[i].get('params'), function (itm) { return itm.Name == self.options.selectedDatatable });
|
---|
| 1085 |
|
---|
| 1086 | if (!table)
|
---|
[9335] | 1087 | continue;
|
---|
| 1088 | var index = table.RowNames.indexOf(this.options.datatableRow);
|
---|
| 1089 | if (index == -1)
|
---|
| 1090 | continue;
|
---|
| 1091 | var entries = [];
|
---|
| 1092 | for (var j = 0; j < table.Value.length; j++) {
|
---|
| 1093 | entries.push(table.Value[j][index]);
|
---|
| 1094 | }
|
---|
| 1095 | values.push(entries);
|
---|
| 1096 | rowNames.push(this.options.datatableRow + ' of Run Number ' + (i + 1));
|
---|
| 1097 | }
|
---|
| 1098 |
|
---|
| 1099 | // render datatable
|
---|
| 1100 | var div = this.preparePlotDiv();
|
---|
| 1101 | var plot = new my.PlotView({ model: values, el: div, rownames: rowNames });
|
---|
| 1102 | this.plot = plot;
|
---|
| 1103 | plot.render();
|
---|
| 1104 | }
|
---|
| 1105 | });
|
---|
| 1106 |
|
---|
| 1107 | // ===================== Plots =============================
|
---|
| 1108 |
|
---|
| 1109 | my.PlotView = Backbone.View.extend({
|
---|
| 1110 | render: function () {
|
---|
| 1111 | this.$el.empty();
|
---|
| 1112 | var plotLabels = undefined;
|
---|
| 1113 | var labelSource = this.model.RowNames ? this.model.RowNames : this.options.rownames;
|
---|
| 1114 | if (labelSource) {
|
---|
| 1115 | plotLabels = [];
|
---|
| 1116 | for (var i = 0; i < labelSource.length; i++) {
|
---|
| 1117 | plotLabels.push({ label: labelSource[i] });
|
---|
| 1118 | }
|
---|
| 1119 | }
|
---|
| 1120 |
|
---|
| 1121 | var values = this.model.Value ? this.model.Value : this.model;
|
---|
| 1122 |
|
---|
| 1123 | var plotValues = [];
|
---|
| 1124 |
|
---|
| 1125 | if (!this.options.transpose) {
|
---|
| 1126 | if ($.isArray(values[0])) {
|
---|
| 1127 | for (var i = 0; i < values.length; i++) {
|
---|
| 1128 | plotValues.push(values[i]);
|
---|
| 1129 | }
|
---|
| 1130 | } else {
|
---|
| 1131 | plotValues.push(values);
|
---|
| 1132 | }
|
---|
| 1133 | }
|
---|
| 1134 | else {
|
---|
| 1135 | if ($.isArray(values[0])) {
|
---|
| 1136 | var columnCount = values[0].length;
|
---|
| 1137 | var colVals = {};
|
---|
| 1138 | for (var i = 0; i < columnCount; i++)
|
---|
| 1139 | colVals[i] = [];
|
---|
| 1140 |
|
---|
| 1141 | for (var i = 0; i < values.length; i++) {
|
---|
| 1142 | for (var j = 0; j < columnCount; j++) {
|
---|
| 1143 | colVals[j].push(values[i][j]);
|
---|
| 1144 | }
|
---|
| 1145 | }
|
---|
| 1146 |
|
---|
| 1147 | for (var i = 0; i < columnCount; i++) {
|
---|
| 1148 | plotValues.push(colVals[i]);
|
---|
| 1149 | }
|
---|
| 1150 | } else {
|
---|
| 1151 | plotValues.push(values);
|
---|
| 1152 | }
|
---|
| 1153 | }
|
---|
| 1154 |
|
---|
| 1155 | if (!this.$el.attr('id')) {
|
---|
| 1156 | this.$el.attr('id', my.PlotView.nextId());
|
---|
| 1157 | }
|
---|
| 1158 |
|
---|
| 1159 | if (!this.$el.css('width'))
|
---|
| 1160 | this.$el.css('width', '500px');
|
---|
| 1161 |
|
---|
| 1162 | this.plot = $.jqplot(this.$el.attr('id'), plotValues, {
|
---|
| 1163 | cursor: {
|
---|
| 1164 | show: true,
|
---|
| 1165 | zoom: true,
|
---|
| 1166 | showTooltip: false
|
---|
| 1167 | },
|
---|
| 1168 | series: plotLabels,
|
---|
| 1169 | seriesDefaults: {
|
---|
| 1170 | lineWidth: 1.5,
|
---|
| 1171 | markerOptions: {
|
---|
| 1172 | size: 2,
|
---|
| 1173 | lineWidth: 2
|
---|
| 1174 | }
|
---|
| 1175 | },
|
---|
| 1176 | legend: {
|
---|
| 1177 | show: true,
|
---|
| 1178 | placement: 'outsideGrid'
|
---|
| 1179 | },
|
---|
| 1180 | highlighter: {
|
---|
| 1181 | show: true,
|
---|
| 1182 | sizeAdjust: 7.5
|
---|
| 1183 | }
|
---|
| 1184 | });
|
---|
| 1185 | },
|
---|
| 1186 | refresh: function () {
|
---|
| 1187 | if (this.plot)
|
---|
| 1188 | this.plot.replot({ resetAxes: true });
|
---|
| 1189 | }
|
---|
| 1190 | },
|
---|
| 1191 | {
|
---|
| 1192 | plotId: 1,
|
---|
| 1193 | nextId: function () {
|
---|
| 1194 | return my.PlotView.plotId++;
|
---|
| 1195 | }
|
---|
| 1196 | });
|
---|
| 1197 |
|
---|
| 1198 | my.BubbleplotView = Backbone.View.extend({
|
---|
| 1199 | render: function () {
|
---|
| 1200 | this.$el.empty();
|
---|
| 1201 |
|
---|
| 1202 | var values = this.model && this.model.Value ? this.model.Value : this.model;
|
---|
| 1203 |
|
---|
| 1204 | var plotValues = [];
|
---|
| 1205 |
|
---|
| 1206 |
|
---|
| 1207 | if (!this.$el.attr('id')) {
|
---|
| 1208 | this.$el.attr('id', my.PlotView.nextId());
|
---|
| 1209 | }
|
---|
| 1210 |
|
---|
| 1211 | if (!this.$el.css('width'))
|
---|
| 1212 | this.$el.css('width', '500px');
|
---|
| 1213 |
|
---|
| 1214 | this.plot = $.jqplot(this.$el.attr('id'), [values], {
|
---|
| 1215 | cursor: {
|
---|
| 1216 | show: true,
|
---|
| 1217 | zoom: true,
|
---|
| 1218 | showTooltip: false
|
---|
| 1219 | },
|
---|
| 1220 | seriesDefaults: { renderer: $.jqplot.BubbleRenderer,
|
---|
| 1221 | rendererOptions: {
|
---|
| 1222 | autoscaleBubbles: this.options.autoscale == true,
|
---|
| 1223 | bubbleGradients: true
|
---|
| 1224 | },
|
---|
| 1225 | shadow: true
|
---|
| 1226 | }
|
---|
| 1227 | });
|
---|
| 1228 | },
|
---|
| 1229 | refresh: function () {
|
---|
| 1230 | if (this.plot)
|
---|
| 1231 | this.plot.replot({ resetAxes: true });
|
---|
| 1232 | }
|
---|
| 1233 | });
|
---|
| 1234 |
|
---|
| 1235 | my.BoxplotView = Backbone.View.extend({
|
---|
| 1236 | render: function () {
|
---|
| 1237 | this.$el.empty();
|
---|
| 1238 | var plotLabels = undefined;
|
---|
| 1239 | if (this.model && this.model.RowNames) {
|
---|
| 1240 | plotLabels = [];
|
---|
| 1241 | for (var i = 0; i < this.model.RowNames.length; i++) {
|
---|
| 1242 | plotLabels.push({ label: this.model.RowNames[i] });
|
---|
| 1243 | }
|
---|
| 1244 | }
|
---|
| 1245 |
|
---|
| 1246 | var values = this.model && this.model.Value ? this.model.Value : this.model;
|
---|
| 1247 | var globalMin = Number.MAX_VALUE;
|
---|
| 1248 | var globalMax = Number.MIN_VALUE;
|
---|
| 1249 |
|
---|
| 1250 | var plotValues = [];
|
---|
| 1251 |
|
---|
| 1252 | for (var key in values) {
|
---|
| 1253 | var entry = values[key];
|
---|
| 1254 | if ($.isArray(entry[0])) {
|
---|
| 1255 | for (var i = 0; i < entry.length; i++) {
|
---|
| 1256 | var cnt = entry[i].length;
|
---|
| 1257 | var mean = _.mean(entry[i]);
|
---|
| 1258 | var median = _.median(entry[i]);
|
---|
| 1259 | var min = _.min(entry[i]);
|
---|
| 1260 | var max = _.max(entry[i]);
|
---|
| 1261 | var sorted = _.sortBy(entry[i], function (num) { return num; });
|
---|
| 1262 | var q1 = percentile(25, sorted);
|
---|
| 1263 | var q3 = percentile(75, sorted);
|
---|
| 1264 | var diff = 1.5 * (q3 - q1);
|
---|
| 1265 |
|
---|
| 1266 | plotValues.push([key, min, q1, median, q3, max]);
|
---|
| 1267 | //plotValues.push(["Sample " + i, percentile(15, sorted), q1, median, q3, percentile(85, sorted)]);
|
---|
| 1268 | if (max > globalMax) globalMax = max;
|
---|
| 1269 | if (min < globalMin) globalMin = min;
|
---|
| 1270 | }
|
---|
| 1271 | } else {
|
---|
| 1272 | var cnt = entry.length;
|
---|
| 1273 | var mean = _.mean(entry);
|
---|
| 1274 | var median = _.median(entry);
|
---|
| 1275 | var min = _.min(entry);
|
---|
| 1276 | var max = _.max(entry);
|
---|
| 1277 | var sorted = _.sortBy(entry, function (num) { return num; });
|
---|
| 1278 | var q1 = percentile(25, sorted);
|
---|
| 1279 | var q3 = percentile(75, sorted);
|
---|
| 1280 | plotValues.push([key, min, q1, median, q3, max]);
|
---|
| 1281 | if (max > globalMax) globalMax = max;
|
---|
| 1282 | if (min < globalMin) globalMin = min;
|
---|
| 1283 | }
|
---|
| 1284 | }
|
---|
| 1285 |
|
---|
| 1286 |
|
---|
| 1287 |
|
---|
| 1288 | if (!this.$el.attr('id')) {
|
---|
| 1289 | this.$el.attr('id', my.PlotView.nextId());
|
---|
| 1290 | }
|
---|
| 1291 |
|
---|
| 1292 | if (!this.$el.css('width'))
|
---|
| 1293 | this.$el.css('width', '500px');
|
---|
| 1294 |
|
---|
| 1295 | this.plot = $.jqplot(this.$el.attr('id'), [plotValues], {
|
---|
| 1296 | cursor: {
|
---|
| 1297 | show: true,
|
---|
| 1298 | zoom: true,
|
---|
| 1299 | showTooltip: false
|
---|
| 1300 | },
|
---|
| 1301 | series: [{ renderer: $.jqplot.BoxplotRenderer, rendererOptions: {}}],
|
---|
| 1302 | axesDefaults: {},
|
---|
| 1303 | axes: {
|
---|
| 1304 | yaxis: {
|
---|
| 1305 | min: globalMin * 0.9,
|
---|
| 1306 | max: globalMax * 1.1
|
---|
| 1307 | },
|
---|
| 1308 | xaxis: {
|
---|
| 1309 | renderer: $.jqplot.CategoryAxisRenderer
|
---|
| 1310 | }
|
---|
| 1311 | },
|
---|
| 1312 | legend: {
|
---|
| 1313 | show: true,
|
---|
| 1314 | placement: 'outsideGrid'
|
---|
| 1315 | },
|
---|
| 1316 | highlighter: {
|
---|
| 1317 | show: true,
|
---|
| 1318 | sizeAdjust: 7.5
|
---|
| 1319 | }
|
---|
| 1320 | });
|
---|
| 1321 | },
|
---|
| 1322 | refresh: function () {
|
---|
| 1323 | if (this.plot)
|
---|
| 1324 | this.plot.replot({ resetAxes: true });
|
---|
| 1325 | }
|
---|
| 1326 | });
|
---|
[9324] | 1327 | return my;
|
---|
| 1328 | } (OAAS_VIEW || {}, Backbone, _, $, OAAS_MODEL)); |
---|