Free cookie consent management tool by TermsFeed Policy Generator

source: branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/experiment.view.js @ 9335

Last change on this file since 9335 was 9335, checked in by fschoepp, 11 years ago

#1888:

  • The Edit-Experiment-Page now correctly displays the experiments again.
  • Disabled key navigation for all wizards.
  • Added a RunCollectionView to render different Plots for the run results of a job.
  • Added a Boxplot-Chart and a Bubble-Chart for rendering the results.
  • Added underscoreAddon.js which provides methods to calculate mean/sum of an JavaScript array/list.
File size: 50.9 KB
Line 
1var 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                    }
17
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                    }
24                },
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);
44                        }
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);
57                    }
58                }
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                }
91            }
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            });
101        }
102    });
103
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        }
123    });
124
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        }
143    });
144
145
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);
155        }
156    });
157
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            }
170
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    });
187
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    });
194
195    my.StepWizardView = Backbone.View.extend({
196        render: function () {
197            var self = this;
198            this.$el.smartWizard({
199                keyNavigation: false,
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;
224        }
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);
240            }
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');
254
255            var hasProblemParameters = typeof data.ProblemParameters !== 'undefined';
256            var dialog = $('<div />');
257            var algoDiv = $('<div />');
258            var problemDiv = $('<div />');
259
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);
265            }
266
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            }
276
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            ]);
286
287            newWizard.appendTo(this.$el);
288            newWizard.smartWizard({
289                keyNavigation: false,
290                onFinish: function () {
291                    self.trigger('parameters-finished');
292                }
293            });
294        }
295    });
296
297    my.ParameterEditableView = Backbone.View.extend({
298        /*events: {
299        'change': 'valueChanged'
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    });
323
324
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    });
358
359
360    my.ParameterDialog = Backbone.View.extend({
361        render: function () {
362            var self = this;
363
364            this.$el.empty();
365            var data = this.model.get('data');
366
367            var hasProblemParameters = typeof data.ProblemParameters !== 'undefined';
368            var dialog = $('<div />');
369
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        }
385    });
386
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 });
404
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'));
418        }
419    });
420
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                    }
431                }
432            });
433        }
434    });
435
436    // ========== Variation Dialog Views ============
437
438    my.VariationDialog = Backbone.View.extend({
439        initialize: function (spec) {
440
441        },
442        events: {
443            'change': 'entrySelected'
444        },
445        render: function () {
446            var self = this;
447            var dialog = $(_.template($('#variationdialog_template').html(), {}));
448
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            }
456
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;
463                }
464
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            });
485
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            });
491
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 {
535                element[index] = {
536                    Name: currentParameter.Name,
537                    Value: currentParameter.Value
538                };
539                if (currentParameter.Options) {
540                    element[index].Options = currentParameter.Options;
541                }
542                this.exhaust(index + 1, element);
543            }
544        }
545    });
546
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');
557
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            }
564
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);
580
581            // create body
582            var body = $('<tbody></tbody>');
583
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);
602            }
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);
620            }
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]);
634            }
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();
645        }
646    });
647
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);
654
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            }
664        }
665    });
666
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 });
683        }
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                }
696            }
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 });
705        }
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];
722                }
723                var tev = new my.TableEditView({ model: this.model.Generated, el: $('div div', this.$el), editable: true });
724                tev.render();
725            }
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    });
769
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    });
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 () {
818            var self = this;
819            var ele = $(_.template($('#runcollection_template').html(), {}));
820            var xAxisSelect = $('.x', ele);
821            var yAxisSelect = $('.y', ele);
822            var results = this.model.models[0].get('results');
823            this.options.datatables = {};
824            var datatableSelect = $('.datatableOptions', ele);
825
826            var names = _.reduce(this.model.models, function (arr, model) {
827                return _.union(arr, _.map(model.get('results'), function (elem) { return elem.Name }));
828            }, []);
829
830            this.bubbleSelect = $('select[class="bubbleSize"]', ele);
831
832            _.each(names, function (name) {
833                var option = $("<option />", { value: name, text: name });
834                option.clone().appendTo(xAxisSelect);
835                option.clone().appendTo(yAxisSelect);
836                option.clone().appendTo(self.bubbleSelect);
837                var rowNames = null;
838                var foundModel = _.find(self.model.models, function (model) {
839                    var entry = _.find(model.get('results'), function (entry) { return entry.Name == name && entry.RowNames });
840                    if (entry)
841                        rowNames = entry.RowNames;
842                    return entry && entry.RowNames;
843                });
844                if (rowNames) {
845                    self.options.datatables[name] = rowNames;
846                    $('<option />', { value: name, text: name }).appendTo(datatableSelect);
847                }
848            });
849
850            // prepare slider settings
851            this.options.bubbleSize = 10;
852            this.options.xAxis = this.model.models[0].get('results')[0].Name;
853            this.options.yAxis = this.options.xAxis;
854            this.options.plot = 'Boxplot';
855            $('div[class="bubbleSlider"]', ele).slider({
856                range: "max",
857                min: 10,
858                max: 40,
859                value: 10,
860                stop: function (event, ui) {
861                    self.options.bubbleSize = ui.value;
862                    self.createPlot();
863                }
864            }).css('width', '120px').css('margin-left', '15px').css('margin-right', '15px').css('display', 'inline-block');
865            ele.appendTo(this.$el);
866
867            // init resizer
868            this.plotWidth = 500;
869            this.plotHeight = 500;
870            var parent = $('div[class="resizer"]', this.$el);
871            this.resizerView = new my.ResizableView({ el: parent });
872            this.listenTo(this.resizerView, 'resized', function (evt) {
873                self.updatePlot(
874         $(evt.target).width() * 0.96,
875         $(evt.target).height() * 0.96
876        );
877            });
878            this.resizerView.render();
879            this.updateRowSelect(datatableSelect.val());
880            this.createPlot();
881            this.updatePlot(
882         600,
883         500
884        );
885        },
886        updateRowSelect: function (name) {
887            if (!name)
888                return;
889            var rowSelect = $('select[class="datatableRow"]', this.$el);
890            rowSelect.empty();
891            var rownames = this.options.datatables[name];
892            for (var i = 0; i < rownames.length; i++) {
893                $('<option />', { value: rownames[i], text: rownames[i] }).appendTo(rowSelect);
894            }
895            this.options.selectedDatatable = name;
896            this.options.datatableRow = rowSelect.val();
897            this.createPlot();
898        },
899        datatableSelected: function (evt) {
900            var value = $(evt.target).val();
901            this.options.selectedDatatable = value;
902            this.updateRowSelect(value);
903        },
904        datatableRowSelected: function (evt) {
905            var value = $(evt.target).val();
906            this.options.datatableRow = value;
907            this.createPlot();
908        },
909        xAxisSelected: function (evt) {
910            var target = $(evt.target);
911            this.options.xAxis = target.val();
912            this.createPlot();
913        },
914        yAxisSelected: function (evt) {
915            var target = $(evt.target);
916            this.options.yAxis = target.val();
917            this.createPlot();
918        },
919        plotSelected: function (evt) {
920            var target = $(evt.target);
921            this.options.plot = target.val();
922            this.createPlot();
923        },
924        bubbleTypeSelected: function (evt) {
925            this.createPlot();
926        },
927        updateVisibility: function (evt) {
928            if (this.options.plot == 'Boxplot') {
929                $('span[class="choice"]', this.$el).show();
930                $('span[class="bubble"]', this.$el).hide();
931                $('span[class="datatable"]', this.$el).hide();
932            } else if (this.options.plot == 'Bubblechart') {
933                $('span[class="choice"]', this.$el).show();
934                $('span[class="bubble"]', this.$el).show();
935                $('span[class="datatable"]', this.$el).hide();
936            } else if (this.options.plot == 'Datatable') {
937                $('span[class="choice"]', this.$el).hide();
938                $('span[class="bubble"]', this.$el).hide();
939                $('span[class="datatable"]', this.$el).show();
940            }
941        },
942        updatePlot: function (width, height) {
943            this.plotDiv.height(height);
944            this.plotDiv.width(width);
945            this.plotWidth = width;
946            this.plotHeight = height;
947            this.plot.refresh();
948        },
949        createPlot: function () {
950            if (this.options.plot && this.options.xAxis && this.options.yAxis) {
951                if (this.options.plot == 'Boxplot') {
952                    this.createBoxplot();
953                } else if (this.options.plot == 'Bubblechart') {
954                    this.createBubblechart();
955                } else if (this.options.plot == 'Datatable') {
956                    this.createDatatable();
957                }
958                this.updateVisibility();
959            }
960        },
961        preparePlotDiv: function () {
962            var parent = $('div[class="plot"]', this.$el);
963            parent.empty();
964            this.plotDiv = $('<div></div>').css('width', this.plotWidth).css('height', this.plotHeight).appendTo(parent);
965            return this.plotDiv;
966        },
967        createBoxplot: function () {
968            var self = this;
969
970            // prepare data for boxplot
971            var values = {};
972            for (var i = 0; i < this.model.models.length; i++) {
973                var xlabel = _.find(this.model.models[i].get('results'), function (itm) { return itm.Name == self.options.xAxis });
974                if (!xlabel)
975                    continue;
976
977                if ($.isArray(xlabel.Value))
978                    values[xlabel.Name + ' = ' + xlabel.Value[0]] = [];
979                else
980                    values[xlabel.Name + ' = ' + xlabel.Value] = [];
981            }
982
983            for (var i = 0; i < this.model.models.length; i++) {
984                var xlabel = _.find(this.model.models[i].get('results'), function (itm) { return itm.Name == self.options.xAxis });
985                var entry = _.find(this.model.models[i].get('results'), function (itm) { return itm.Name == self.options.yAxis });
986                if (!xlabel || !entry || !entry.Value)
987                    continue;
988
989                var index = xlabel.Name + ' = ' + ($.isArray(xlabel.Value) ? xlabel.Value[0] : xlabel.Value);
990
991                if ($.isArray(entry.Value))
992                    values[index].push(entry.Value[0]);
993                else
994                    values[index].push(entry.Value);
995            }
996
997            // create boxplot
998            var div = this.preparePlotDiv();
999            var boxPlot = new my.BoxplotView({ model: values, el: div });
1000            this.plot = boxPlot;
1001            boxPlot.render();
1002        },
1003        createBubblechart: function () {
1004            var self = this;
1005
1006            // prepare data for bubble chart
1007            var values = [];
1008            var autoscaleBubbles = this.bubbleSelect.val() != 'Constant';
1009            for (var i = 0; i < this.model.models.length; i++) {
1010                var xValue = _.find(this.model.models[i].get('results'), function (itm) { return itm.Name == self.options.xAxis });
1011                var yValue = _.find(this.model.models[i].get('results'), function (itm) { return itm.Name == self.options.yAxis });
1012                if (!xValue || !yValue)
1013                    continue;
1014
1015                // determine size
1016                var size = 0;
1017                if (this.bubbleSelect.val() == 'Constant') {
1018                    size = this.options.bubbleSize;
1019                } else {
1020                    var valueProvider = _.find(this.model.models[i].get('results'), function (itm) { return itm.Name == self.bubbleSelect.val(); });
1021                    if (!valueProvider) {
1022                        size = this.options.bubbleSize;
1023                    } else {
1024                        var value = valueProvider.Value;
1025                        while ($.isArray(value)) {
1026                            value = value[0];
1027                        }
1028                        size = parseInt(value.replace(/,/, '.'));
1029                        if (isNaN(size))
1030                            size = this.options.bubbleSize;
1031                    }
1032                }
1033
1034                // add element
1035                var xFloat = parseFloat(xValue.Value.replace(/,/, '.'));
1036                var yFloat = parseFloat(yValue.Value.replace(/,/, '.'));
1037
1038                values.push([xFloat, yFloat, size, 'Run' + (i + 1)]);
1039            }
1040
1041            // render bubble chart
1042            var div = this.preparePlotDiv();
1043            var bubblePlot = new my.BubbleplotView({ model: values, el: div, autoscale: autoscaleBubbles });
1044            this.plot = bubblePlot;
1045            bubblePlot.render();
1046        },
1047        createDatatable: function () {
1048            if (!this.options.selectedDatatable)
1049                return;
1050            var self = this;
1051
1052            // prepare data for datatable
1053            var values = [];
1054            var rowNames = [];
1055            for (var i = 0; i < this.model.models.length; i++) {
1056                var table = _.find(this.model.models[i].get('results'), function (itm) { return itm.Name == self.options.selectedDatatable });
1057                if (!table)
1058                    continue;
1059                var index = table.RowNames.indexOf(this.options.datatableRow);
1060                if (index == -1)
1061                    continue;
1062                var entries = [];
1063                for (var j = 0; j < table.Value.length; j++) {
1064                    entries.push(table.Value[j][index]);
1065                }
1066                values.push(entries);
1067                rowNames.push(this.options.datatableRow + ' of Run Number ' + (i + 1));
1068            }
1069
1070            // render datatable
1071            var div = this.preparePlotDiv();
1072            var plot = new my.PlotView({ model: values, el: div, rownames: rowNames });
1073            this.plot = plot;
1074            plot.render();
1075        }
1076    });
1077
1078    // ===================== Plots =============================
1079
1080    my.PlotView = Backbone.View.extend({
1081        render: function () {
1082            this.$el.empty();
1083            var plotLabels = undefined;
1084            var labelSource = this.model.RowNames ? this.model.RowNames : this.options.rownames;
1085            if (labelSource) {
1086                plotLabels = [];
1087                for (var i = 0; i < labelSource.length; i++) {
1088                    plotLabels.push({ label: labelSource[i] });
1089                }
1090            }
1091
1092            var values = this.model.Value ? this.model.Value : this.model;
1093
1094            var plotValues = [];
1095
1096            if (!this.options.transpose) {
1097                if ($.isArray(values[0])) {
1098                    for (var i = 0; i < values.length; i++) {
1099                        plotValues.push(values[i]);
1100                    }
1101                } else {
1102                    plotValues.push(values);
1103                }
1104            }
1105            else {
1106                if ($.isArray(values[0])) {
1107                    var columnCount = values[0].length;
1108                    var colVals = {};
1109                    for (var i = 0; i < columnCount; i++)
1110                        colVals[i] = [];
1111
1112                    for (var i = 0; i < values.length; i++) {
1113                        for (var j = 0; j < columnCount; j++) {
1114                            colVals[j].push(values[i][j]);
1115                        }
1116                    }
1117
1118                    for (var i = 0; i < columnCount; i++) {
1119                        plotValues.push(colVals[i]);
1120                    }
1121                } else {
1122                    plotValues.push(values);
1123                }
1124            }
1125
1126            if (!this.$el.attr('id')) {
1127                this.$el.attr('id', my.PlotView.nextId());
1128            }
1129
1130            if (!this.$el.css('width'))
1131                this.$el.css('width', '500px');
1132
1133            this.plot = $.jqplot(this.$el.attr('id'), plotValues, {
1134                cursor: {
1135                    show: true,
1136                    zoom: true,
1137                    showTooltip: false
1138                },
1139                series: plotLabels,
1140                seriesDefaults: {
1141                    lineWidth: 1.5,
1142                    markerOptions: {
1143                        size: 2,
1144                        lineWidth: 2
1145                    }
1146                },
1147                legend: {
1148                    show: true,
1149                    placement: 'outsideGrid'
1150                },
1151                highlighter: {
1152                    show: true,
1153                    sizeAdjust: 7.5
1154                }
1155            });
1156        },
1157        refresh: function () {
1158            if (this.plot)
1159                this.plot.replot({ resetAxes: true });
1160        }
1161    },
1162  {
1163      plotId: 1,
1164      nextId: function () {
1165          return my.PlotView.plotId++;
1166      }
1167  });
1168
1169    my.BubbleplotView = Backbone.View.extend({
1170        render: function () {
1171            this.$el.empty();
1172
1173            var values = this.model && this.model.Value ? this.model.Value : this.model;
1174
1175            var plotValues = [];
1176
1177
1178            if (!this.$el.attr('id')) {
1179                this.$el.attr('id', my.PlotView.nextId());
1180            }
1181
1182            if (!this.$el.css('width'))
1183                this.$el.css('width', '500px');
1184
1185            this.plot = $.jqplot(this.$el.attr('id'), [values], {
1186                cursor: {
1187                    show: true,
1188                    zoom: true,
1189                    showTooltip: false
1190                },
1191                seriesDefaults: { renderer: $.jqplot.BubbleRenderer,
1192                    rendererOptions: {
1193                        autoscaleBubbles: this.options.autoscale == true,
1194                        bubbleGradients: true
1195                    },
1196                    shadow: true
1197                }
1198            });
1199        },
1200        refresh: function () {
1201            if (this.plot)
1202                this.plot.replot({ resetAxes: true });
1203        }
1204    });
1205
1206    my.BoxplotView = Backbone.View.extend({
1207        render: function () {
1208            this.$el.empty();
1209            var plotLabels = undefined;
1210            if (this.model && this.model.RowNames) {
1211                plotLabels = [];
1212                for (var i = 0; i < this.model.RowNames.length; i++) {
1213                    plotLabels.push({ label: this.model.RowNames[i] });
1214                }
1215            }
1216
1217            var values = this.model && this.model.Value ? this.model.Value : this.model;
1218            var globalMin = Number.MAX_VALUE;
1219            var globalMax = Number.MIN_VALUE;
1220
1221            var plotValues = [];
1222
1223            for (var key in values) {
1224                var entry = values[key];
1225                if ($.isArray(entry[0])) {
1226                    for (var i = 0; i < entry.length; i++) {
1227                        var cnt = entry[i].length;
1228                        var mean = _.mean(entry[i]);
1229                        var median = _.median(entry[i]);
1230                        var min = _.min(entry[i]);
1231                        var max = _.max(entry[i]);
1232                        var sorted = _.sortBy(entry[i], function (num) { return num; });
1233                        var q1 = percentile(25, sorted);
1234                        var q3 = percentile(75, sorted);
1235                        var diff = 1.5 * (q3 - q1);
1236
1237                        plotValues.push([key, min, q1, median, q3, max]);
1238                        //plotValues.push(["Sample " + i, percentile(15, sorted), q1, median, q3, percentile(85, sorted)]);
1239                        if (max > globalMax) globalMax = max;
1240                        if (min < globalMin) globalMin = min;
1241                    }
1242                } else {
1243                    var cnt = entry.length;
1244                    var mean = _.mean(entry);
1245                    var median = _.median(entry);
1246                    var min = _.min(entry);
1247                    var max = _.max(entry);
1248                    var sorted = _.sortBy(entry, function (num) { return num; });
1249                    var q1 = percentile(25, sorted);
1250                    var q3 = percentile(75, sorted);
1251                    plotValues.push([key, min, q1, median, q3, max]);
1252                    if (max > globalMax) globalMax = max;
1253                    if (min < globalMin) globalMin = min;
1254                }
1255            }
1256
1257
1258
1259            if (!this.$el.attr('id')) {
1260                this.$el.attr('id', my.PlotView.nextId());
1261            }
1262
1263            if (!this.$el.css('width'))
1264                this.$el.css('width', '500px');
1265
1266            this.plot = $.jqplot(this.$el.attr('id'), [plotValues], {
1267                cursor: {
1268                    show: true,
1269                    zoom: true,
1270                    showTooltip: false
1271                },
1272                series: [{ renderer: $.jqplot.BoxplotRenderer, rendererOptions: {}}],
1273                axesDefaults: {},
1274                axes: {
1275                    yaxis: {
1276                        min: globalMin * 0.9,
1277                        max: globalMax * 1.1
1278                    },
1279                    xaxis: {
1280                        renderer: $.jqplot.CategoryAxisRenderer
1281                    }
1282                },
1283                legend: {
1284                    show: true,
1285                    placement: 'outsideGrid'
1286                },
1287                highlighter: {
1288                    show: true,
1289                    sizeAdjust: 7.5
1290                }
1291            });
1292        },
1293        refresh: function () {
1294            if (this.plot)
1295                this.plot.replot({ resetAxes: true });
1296        }
1297    });
1298    return my;
1299} (OAAS_VIEW || {}, Backbone, _, $, OAAS_MODEL));
Note: See TracBrowser for help on using the repository browser.