Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / static / snf / js / ui / web / ui_create_view.js @ 3b463c5a

History | View | Annotate | Download (54.5 kB)

1
// Copyright 2011 GRNET S.A. All rights reserved.
2
// 
3
// Redistribution and use in source and binary forms, with or
4
// without modification, are permitted provided that the following
5
// conditions are met:
6
// 
7
//   1. Redistributions of source code must retain the above
8
//      copyright notice, this list of conditions and the following
9
//      disclaimer.
10
// 
11
//   2. Redistributions in binary form must reproduce the above
12
//      copyright notice, this list of conditions and the following
13
//      disclaimer in the documentation and/or other materials
14
//      provided with the distribution.
15
// 
16
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
// POSSIBILITY OF SUCH DAMAGE.
28
// 
29
// The views and conclusions contained in the software and
30
// documentation are those of the authors and should not be
31
// interpreted as representing official policies, either expressed
32
// or implied, of GRNET S.A.
33
// 
34

    
35
;(function(root){
36

    
37
    // root
38
    var root = root;
39
    
40
    // setup namepsaces
41
    var snf = root.synnefo = root.synnefo || {};
42
    var models = snf.models = snf.models || {}
43
    var storage = snf.storage = snf.storage || {};
44
    var ui = snf.ui = snf.ui || {};
45
    var util = snf.util = snf.util || {};
46

    
47
    var views = snf.views = snf.views || {}
48

    
49
    // shortcuts
50
    var bb = root.Backbone;
51

    
52

    
53
    views.VMCreationPasswordView = views.Overlay.extend({
54
        view_id: "creation_password_view",
55
        content_selector: "#creation-password-overlay",
56
        css_class: 'overlay-password overlay-info',
57
        overlay_id: "creation-password-overlay",
58

    
59
        subtitle: "",
60
        title: "Machine password",
61

    
62
        initialize: function(options) {
63
            views.FeedbackView.__super__.initialize.apply(this, arguments);
64
            _.bindAll(this, 'show_password');
65

    
66
            this.password = this.$("#new-machine-password");
67
            this.copy = this.$(".clipboard");
68

    
69
            this.$(".show-machine").click(_.bind(function(){
70
                if (this.$(".show-machine").hasClass("in-progress")) {
71
                    return;
72
                }
73
                this.hide();
74
                snf.ui.main.show_vm_details(storage.vms.get(this.vm_id));
75
            }, this));
76

    
77
            _.bindAll(this, "handle_vm_added");
78
            storage.vms.bind("add", this.handle_vm_added);
79
            this.password.text("");
80
        },
81

    
82
        handle_vm_added: function() {
83
            this.$(".show-machine").removeClass("in-progress");
84
        },
85
        
86
        show_password: function() {
87
            this.$(".show-machine").addClass("in-progress");
88
            this.password.text(this.pass);
89
            if (storage.vms.get(this.vm_id)) {
90
                this.$(".show-machine").removeClass("in-progress");
91
            }
92
            
93
            this.clip = new snf.util.ClipHelper(this.copy, this.pass);
94
        },
95

    
96
        onClose: function() {
97
            this.password.text("");
98
            this.vm_id = undefined;
99
            try { delete this.clip; } catch (err) {};
100
        },
101
        
102
        beforeOpen: function() {
103
            this.copy.empty();
104
        },
105
        
106
        onOpen: function() {
107
            this.show_password();
108
        },
109

    
110
        show: function(pass, vm_id) {
111
            this.pass = pass;
112
            this.vm_id = vm_id;
113
            
114
            views.VMCreationPasswordView.__super__.show.apply(this, arguments);
115
        }
116
    })
117

    
118

    
119
    
120
    views.CreateVMStepView = views.View.extend({
121
        step: "1",
122
        title: "Image",
123
        submit: false,
124

    
125
        initialize: function(view) {
126
            this.parent = view;
127
            this.el = view.$("div.create-step-cont.step-" + this.step);
128
            this.header = this.$(".step-header .step-" + this.step);
129
            this.view_id = "create_step_" + this.step;
130

    
131
            views.CreateVMStepView.__super__.initialize.apply(this);
132
        },
133

    
134
        show: function() {
135
            // show current
136
            this.el.show();
137
            this.header.addClass("current");
138
            this.header.show();
139
            this.update_layout();
140
        },
141

    
142
        reset: function() {
143
        }
144
    })
145

    
146
    views.CreateImageSelectView = views.CreateVMStepView.extend({
147

    
148
        initialize: function() {
149
            views.CreateImageSelectView.__super__.initialize.apply(this, arguments);
150

    
151
            // elements
152
            this.images_list_cont = this.$(".images-list-cont");
153
            this.images_list = this.$(".images-list-cont ul");
154
            this.image_details = this.$(".images-info-cont");
155
            this.image_details_desc = this.$(".images-info-cont .description p");
156
            this.image_details_title = this.$(".images-info-cont h4");
157
            this.image_details_size = this.$(".images-info-cont .size p");
158
            this.image_details_os = this.$(".images-info-cont .os p");
159
            this.image_details_kernel = this.$(".images-info-cont .kernel p");
160
            this.image_details_gui = this.$(".images-info-cont .gui p");
161
            this.image_details_vm = this.$(".images-info-cont .vm-name p");
162

    
163
            this.categories_list = this.$(".category-filters");
164
            
165
            // params initialization
166
            this.type_selections = {"system": "System"};
167
            this.type_selections_order = ['system'];
168
            
169
            this.images_storage = snf.storage.images;
170

    
171
            // apply image service specific image types
172
            if (this.images_storage.type_selections) {
173
                this.type_selections = _.extend(
174
                    this.images_storage.type_selections,
175
                    this.type_selections)
176

    
177
                this.type_selections_order = this.images_storage.type_selections_order;
178
            }
179

    
180
            this.selected_type = undefined;
181
            this.selected_categories = [];
182

    
183
            this.images = [];
184
            this.images_ids = [];
185
            this.custom_images = [];
186

    
187
            // handlers initialization
188
            this.create_types_selection_options();
189
            this.init_handlers();
190
            this.init_position();
191
        },
192
        
193
        init_position: function() {
194
            //this.el.css({position: "absolute"});
195
            //this.el.css({top:"10px"})
196
        },
197
        
198
        init_handlers: function() {
199
            var self = this;
200
            this.types.live("click", function() {
201
                self.select_type($(this).attr("id").replace("type-select-",""));
202
            });
203
            
204
            this.image_details.find(".hide").click(_.bind(function(){
205
                this.hide_image_details();
206
            }, this));
207

    
208
            this.$(".register-custom-image").live("click", function(){
209
                var confirm_close = true;
210
                if (confirm_close) {
211
                    snf.ui.main.custom_images_view.show(self.parent);
212
                } else {
213
                }
214
            })
215

    
216
            $(".image-warning .confirm").bind('click', function(){
217
                $(".image-warning").hide();
218
                $(".create-controls").show();
219
            })
220
        },
221

    
222
        update_images: function(images) {
223
            this.images = images;
224
            this.images_ids = _.map(this.images, function(img){return img.id});
225
            return this.images;
226
        },
227

    
228
        create_types_selection_options: function() {
229
            var list = this.$("ul.type-filter");
230
            _.each(this.type_selections_order, _.bind(function(key) {
231
                list.append('<li id="type-select-{0}">{1}</li>'.format(key, this.type_selections[key]));
232
            }, this));
233
            this.types = this.$(".type-filter li");
234
        },
235

    
236
        update_layout: function() {
237
            if (!this.selected_type) {
238
                this.selected_type = _.keys(this.type_selections)[0];
239
            }
240
            this.select_type(this.selected_type);
241
        },
242
        
243
        get_categories: function(images) {
244
            return [];
245
            return ["Desktop", "Server", "Linux", "Windows"];
246
        },
247

    
248
        reset_categories: function() {
249
            var categories = this.get_categories(this.images);
250
            this.categories_list.find("li").remove();
251

    
252
            _.each(categories, _.bind(function(cat) {
253
                var el = $("<li />");
254
                el.text(cat);
255
                this.categories_list.append(el);
256
            }, this));
257

    
258
            if (!categories.length) { 
259
                this.categories_list.parent().find(".clear").hide();
260
                this.categories_list.parent().find(".empty").show();
261
            } else {
262
                this.categories_list.parent().find(".clear").show();
263
                this.categories_list.parent().find(".empty").hide();
264
            }
265
        },
266
        
267
        show_loading_view: function() {
268
            this.$(".images-list-cont .empty").hide();
269
            this.images_list.hide();
270
            this.$(".images-list-cont .loading").show();
271
            this.$(".images-list-cont .images-list").hide();
272
            this.reset_categories();
273
            this.update_images([]);
274
            this.reset_images();
275
            this.hide_list_loading();
276
        },
277

    
278
        hide_loading_view: function(images) {
279
            this.$(".images-list-cont .loading").hide();
280
            this.$(".images-list-cont .images-list").show();
281
            this.reset_categories();
282
            this.update_images(images);
283
            this.reset_images();
284
            this.select_image(this.selected_image);
285
            this.hide_list_loading();
286
            $(".custom-image-help").hide();
287
            if (this.selected_type == 'personal' && !images.length) {
288
                $(".custom-image-help").show();
289
            }
290

    
291
        },
292

    
293
        select_type: function(type) {
294
            this.selected_type = type;
295
            this.types.removeClass("selected");
296
            this.types.filter("#type-select-" + this.selected_type).addClass("selected");
297
            this.images_storage.update_images_for_type(
298
                this.selected_type, 
299
                _.bind(this.show_loading_view, this), 
300
                _.bind(this.hide_loading_view, this)
301
            );
302

    
303
            this.update_layout_for_type(type);
304
        },
305

    
306
        update_layout_for_type: function(type) {
307
            if (type != "system") {
308
                this.$(".custom-action").hide();
309
            } else {
310
                this.$(".custom-action").hide();
311
            }
312

    
313
        },
314

    
315
        show_list_loading: function() {
316
            this.$(".images-list-cont").addClass("loading");
317
        },
318

    
319
        hide_list_loading: function() {
320
            this.$(".images-list-cont").removeClass("loading");
321
        },
322
        
323
        display_warning_for_image: function(image) {
324
          if (image && !image.is_system_image() && !image.owned_by(synnefo.user)) {
325
            $(".create-vm .image-warning").show();
326
            $(".create-controls").hide();
327
          } else {
328
            $(".create-vm .image-warning").hide();
329
            $(".create-controls").show();
330
          }
331
        },
332

    
333
        select_image: function(image) {
334
            if (image && image.get('id') && !_.include(this.images_ids, image.get('id'))) {
335
                image = undefined;
336
            }
337
            if (!image && this.images_ids.length) {
338
                if (this.selected_image && this.images_ids.indexOf(this.selected_image.id) > -1) {
339
                    image = this.selected_image;
340
                } else {
341
                    image = this.images_storage.get(this.images_ids[0]);
342
                }
343
            }
344
             
345
            // no images select null image so that next button gets hidden
346
            if (!this.images_ids.length) { image = undefined };
347
            
348
            if ((!this.selected_image && image) || (this.selected_image != image))
349
                this.trigger("change", image);
350
                this.display_warning_for_image(image);
351

    
352
            this.selected_image = image;
353
                
354
            if (image) {
355
                this.images_list.find(".image-details").removeClass("selected");
356
                this.images_list.find(".image-details#create-vm-image-" + this.selected_image.id).addClass("selected");
357
                this.update_image_details(image);
358

    
359
            } else {
360
            }
361

    
362
            this.image_details.hide();
363
            this.validate();
364
        },
365

    
366
        update_image_details: function(image) {
367
            this.image_details_desc.hide().parent().hide();
368
            if (image.get_description()) {
369
                this.image_details_desc.html(image.get_description(false)).show().parent().show();
370
            }
371
            var img = snf.ui.helpers.os_icon_tag(image.escape("OS"))
372
            if (image.get("name")) {
373
                this.image_details_title.html(img + image.escape("name")).show().parent().show();
374
            }
375
            
376
            var extra_details = this.image_details.find(".extra-details");
377
            // clean prevously added extra details
378
            extra_details.find(".image-detail").remove();
379
            
380
            var skip_keys = ['description', 'sortorder']
381
            var meta_keys = ['owner', 'OS', 'kernel', 'GUI'];
382
            var detail_tpl = ('<div class="clearfix image-detail {2}">' +
383
                             '<span class="title clearfix">{0}' +
384
                             '<span class="custom">custom</span></span>' +
385
                             '<p class="value">{1}</p>' + 
386
                             '</div>');
387
            meta_keys = _.union(meta_keys, this.images_storage.display_metadata || []);
388
            
389
            var append_metadata_row = function(key, is_extra) {
390
                var value;
391
                var method = 'get_' + key.toLowerCase();
392
                var display_method = 'display_' + key.toLowerCase();
393
                 
394
                if (image[display_method]) {
395
                    value = image[display_method]();
396
                } else if (image[method]) {
397
                    value = image[method]();
398
                } else {
399
                    value = image.get(key);
400

    
401
                    if (!value) {
402
                        value = image.get_meta(key);
403
                    }
404
                }
405
                    
406
                if (!value) { return; }
407
                 
408
                var label = this.images_storage.meta_labels[key];
409
                if (!label) {
410
                    var label = _(key.replace(/_/g," ")).capitalize();
411
                }
412
                var row_cls = key.toLowerCase();
413
                if (is_extra) { row_cls += " extra-meta" };
414
                extra_details.append(detail_tpl.format(_.escape(label), value, row_cls));
415
            }
416

    
417
            _.each(meta_keys, function(key) {
418
                append_metadata_row.apply(this, [key]);
419
            }, this);
420
            
421
            if (synnefo.storage.images.display_extra_metadata) {
422
                _.each(image.get('metadata').values, function(value, key) {
423
                    if (!_.contains(meta_keys, key) && 
424
                        !_.contains(meta_keys, key.toLowerCase()) &&
425
                        !_.contains(meta_keys, key.toUpperCase()) &&
426
                        !_.contains(skip_keys, key)) {
427
                            append_metadata_row.apply(this, [key, true]);
428
                    }
429
                }, this);
430
            }
431
        },
432

    
433
        reset_images: function() {
434
            this.images_list.find("li").remove();
435
            _.each(this.images, _.bind(function(img){
436
                this.add_image(img);
437
            }, this))
438
            
439
            if (this.images.length) {
440
                this.images_list.parent().find(".empty").hide();
441
                this.images_list.show();
442
            } else {
443
                this.images_list.parent().find(".empty").show();
444
                this.images_list.hide();
445
            }
446

    
447
            var self = this;
448
            this.images_list.find(".image-details").click(function(){
449
                self.select_image($(this).data("image"));
450
            });
451
            
452
        },
453

    
454
        show: function() {
455
            this.image_details.hide();
456
            this.parent.$(".create-controls").show();
457

    
458
            views.CreateImageSelectView.__super__.show.apply(this, arguments);
459
        },
460

    
461
        add_image: function(img) {
462
            var image = $(('<li id="create-vm-image-{1}"' +
463
                           'class="image-details clearfix">{2}{0}'+
464
                           '<span class="show-details">details</span>'+
465
                           '<span class="size"><span class="prepend">by </span>{5}</span>' + 
466
                           '<span class="owner">' +
467
                           '<span class="prepend"></span>' +
468
                           '{3}</span>' + 
469
                           '<p>{4}</p>' +
470
                           '</li>').format(img.escape("name"), 
471
                                                  img.id, 
472
                                                  snf.ui.helpers.os_icon_tag(img.escape("OS")),
473
                                                  _.escape(img.get_readable_size()),
474
                                                  util.truncate(img.get_description(false), 35),
475
                                                  _.escape(img.display_owner())));
476
            image.data("image", img);
477
            image.data("image_id", img.id);
478
            this.images_list.append(image);
479
            image.find(".show-details").click(_.bind(function(e){
480
                e.preventDefault();
481
                e.stopPropagation();
482
                this.show_image_details(img);
483
            }, this))
484
        },
485
            
486
        hide_image_details: function() {
487
            this.image_details.fadeOut(200);
488
            this.parent.$(".create-controls").show();
489
        },
490

    
491
        show_image_details: function(img) {
492
            this.parent.$(".create-controls").hide();
493
            this.update_image_details(img);
494
            this.image_details.fadeIn(100);
495
        },
496

    
497
        reset: function() {
498
            this.selected_image = false;
499
            this.select_type("system");
500
        },
501

    
502
        get: function() {
503
            return {'image': this.selected_image};
504
        },
505

    
506
        validate: function() {
507
            if (!this.selected_image) {
508
                this.parent.$(".form-action.next").hide();
509
            } else {
510
                this.parent.$(".form-action.next").show();
511
            }
512
        }
513
    });
514

    
515
    views.CreateFlavorSelectView = views.CreateVMStepView.extend({
516
        step: 2,
517
        initialize: function() {
518
            views.CreateFlavorSelectView.__super__.initialize.apply(this, arguments);
519
            this.parent.bind("image:change", _.bind(this.handle_image_change, this));
520

    
521
            this.cpus = this.$(".flavors-cpu-list");
522
            this.disks = this.$(".flavors-disk-list");
523
            this.disk_templates = this.$(".flavors-disk-template-list");
524
            this.mems = this.$(".flavors-mem-list");
525

    
526
            this.predefined_flavors = SUGGESTED_FLAVORS;
527
            this.predefined_flavors_keys = _.keys(SUGGESTED_FLAVORS);
528
            this.predefined_flavors_keys = _.sortBy(this.predefined_flavors_keys, _.bind(function(k){
529
                var flv = this.predefined_flavors[k];
530
                return (flv.ram * flv.cpu * flv.disk);
531
            }, this));
532

    
533
            this.predefined = this.$(".predefined-list");
534
        },
535

    
536
        handle_image_change: function(data) {
537
            this.current_image = data;
538
            this.update_valid_predefined();
539
            this.current_flavor = undefined;
540
            this.update_flavors_data();
541
            this.update_predefined_flavors();
542
            this.reset_flavors();
543
            this.update_layout();
544
        },
545

    
546
        validate_selected_flavor: function() {
547
            if (!this.flavor_is_valid(this.current_flavor)) {
548
                this.select_valid_flavor();
549
            }
550
        },
551

    
552
        reset_flavors: function() {
553
            this.$(".flavor-opts-list .option").remove();
554
            this.create_flavors();
555
        },
556

    
557
        update_predefined_flavors: function() {
558
            this.predefined.find("li").remove();
559
            _.each(this.predefined_flavors_keys, _.bind(function(key) {
560
                var val = this.predefined_flavors[key];
561
                var el = $(('<li class="predefined-selection" id="predefined-flavor-{0}">' +
562
                           '{1}</li>').format(key, _.escape(_(key).capitalize())));
563

    
564
                this.predefined.append(el);
565
                el.data({flavor: storage.flavors.get_flavor(val.cpu, val.ram, val.disk, val.disk_template, this.flavors)});
566
                el.click(_.bind(function() {
567
                    this.handle_predefined_click(el);
568
                }, this))
569
            }, this));
570
            this.update_valid_predefined();
571
        },
572

    
573
        handle_predefined_click: function(el) {
574
            if (el.hasClass("disabled")) { return };
575
            this.set_current(el.data("flavor"));
576
        },
577

    
578
        select_valid_flavor: function() {
579
            var found = false;
580
            var self = this;
581

    
582
            _.each(["cpu", "mem", "disk"], function(t) {
583
              var el = $(".flavor-options."+t);
584
              var all = el.find(".flavor-opts-list li").length;
585
              var disabled = el.find(".flavor-opts-list li.disabled").length;
586
              if (disabled >= all) {
587
                el.find("h4").addClass("error");
588
              } else {
589
                el.find("h4").removeClass("error");
590
              }
591
            })
592

    
593
            _.each(this.flavors, function(flv) {
594
                if (self.flavor_is_valid(flv)) {
595
                    found = flv;
596
                    return false;
597
                }
598
            });
599
            
600
            if (found) {
601
                this.set_current(found);
602
            } else {
603
                this.current_flavor = undefined;
604
                this.validate();
605
                this.$("li.predefined-selection").addClass("disabled");
606
                this.$(".flavor-opts-list li").removeClass("selected");
607
            }
608
        },
609

    
610
        update_valid_predefined: function() {
611
            this.update_unavailable_values();
612
            var self = this;
613
            this.valid_predefined = _.select(_.map(this.predefined_flavors, function(flv, key){
614
                var existing = storage.flavors.get_flavor(flv.cpu, flv.ram, flv.disk, flv.disk_template, self.flavors);
615
                // non existing
616
                if (!existing) {
617
                    return false;
618
                }
619
                
620
                // not available for image
621
                if (self.unavailable_values && self.unavailable_values.disk.indexOf(existing.get_disk_size()) > -1) {
622
                    return false
623
                }
624

    
625
                return key;
626
            }), function(ret) { return ret });
627
            
628
            $("li.predefined-selection").addClass("disabled");
629
            _.each(this.valid_predefined, function(key) {
630
                $("#predefined-flavor-" + key).removeClass("disabled");
631
            })
632
        },
633

    
634
        update_selected_predefined: function() {
635
            var self = this;
636
            this.predefined.find("li").removeClass("selected");
637

    
638
            _.each(this.valid_predefined, function(key){
639
                var flv = self.predefined_flavors[key];
640
                var exists = storage.flavors.get_flavor(flv.cpu, flv.ram, flv.disk, flv.disk_template, self.flavors);
641

    
642
                if (exists && (exists.id == self.current_flavor.id)) {
643
                    $("#predefined-flavor-" + key).addClass("selected");
644
                }
645
            })
646
        },
647
        
648
        update_flavors_data: function() {
649
            this.flavors = storage.flavors.active();
650
            this.flavors_data = storage.flavors.get_data(this.flavors);
651
            
652
            var self = this;
653
            var set = false;
654
            
655
            // FIXME: validate current flavor
656
            
657
            if (!this.current_flavor) {
658
                _.each(this.valid_predefined, function(key) {
659
                    var flv = self.predefined_flavors[key];
660
                    var exists = storage.flavors.get_flavor(flv.cpu, flv.ram, flv.disk, flv.disk_template, self.flavors);
661
                    if (exists && !set) {
662
                        self.set_current(exists);
663
                        set = true;
664
                    }
665
                })
666
            }
667

    
668
            this.update_unavailable_values();
669
        },
670

    
671
        update_unavailable_values: function() {
672
            
673
            var unavailable = {disk:[], ram:[], cpu:[]}
674
            var user_excluded = {disk:[], ram:[], cpu:[]}
675
            var image_excluded = {disk:[], ram:[], cpu:[]}
676

    
677
            if (this.current_image) {
678
              image_excluded = storage.flavors.unavailable_values_for_image(this.current_image);
679
            }
680

    
681
            if (snf.user.quota) {
682
              quotas = this.get_vm_params_quotas();
683
              user_excluded = storage.flavors.unavailable_values_for_quotas(quotas);
684
            }
685

    
686
            unavailable.disk = user_excluded.disk.concat(image_excluded.disk);
687
            unavailable.ram = user_excluded.ram.concat(image_excluded.ram);
688
            unavailable.cpu = user_excluded.cpu.concat(image_excluded.cpu);
689
            
690
            this.unavailable_values = unavailable;
691
        },
692
        
693
        get_vm_params_quotas: function() {
694
          var quota = {
695
            'ram': snf.user.quota.get_available('cyclades.ram'),
696
            'cpu': snf.user.quota.get_available('cyclades.cpu'),
697
            'disk': snf.user.quota.get_available('cyclades.disk')
698
          }
699
          return quota;
700
        },
701

    
702
        flavor_is_valid: function(flv) {
703
            if (!flv) { return false };
704

    
705
            var existing = storage.flavors.get_flavor(flv.get("cpu"), flv.get("ram"), flv.get("disk"), flv.get("disk_template"), this.flavors);
706
            if (!existing) { return false };
707
            
708
            if (this.unavailable_values && (this.unavailable_values.disk.indexOf(parseInt(flv.get("disk")) * 1000) > -1)) {
709
                return false;
710
            }
711
            if (this.unavailable_values && (this.unavailable_values.ram.indexOf(parseInt(flv.get("ram"))) > -1)) {
712
                return false;
713
            }
714
            if (this.unavailable_values && (this.unavailable_values.cpu.indexOf(parseInt(flv.get("cpu"))) > -1)) {
715
                return false;
716
            }
717
            return true;
718
        },
719
            
720
        set_valid_current_for: function(t, val) {
721
            var found = this.flavors[0];
722
            _.each(this.flavors, function(flv) {
723
                if (flv.get(t) == val) {
724
                    found = flv;
725
                }
726
            });
727

    
728
            this.set_current(found);
729
            this.validate_selected_flavor();
730
        },
731

    
732
        set_current: function(flv) {
733

    
734
            if (!flv) {
735
                // user clicked on invalid combination
736
                // force the first available choice for the
737
                // type of option he last clicked
738
                this.set_valid_current_for.apply(this, this.last_choice);
739
                return;
740
            }
741

    
742
            this.current_flavor = flv;
743
            this.trigger("change");
744
            if (this.current_flavor) {
745
                this.update_selected_flavor();
746
                this.update_selected_predefined();
747
            }
748
            
749
            this.validate();
750
        },
751
        
752
        select_default_flavor: function() {
753
               
754
        },
755

    
756
        update_selected_from_ui: function() {
757
            this.set_current(this.ui_selected());
758
        },
759
        
760
        update_disabled_flavors: function() {
761
            this.$(".flavor-options.disk li").removeClass("disabled");
762
            if (!this.unavailable_values) { return }
763
            
764
            this.$("#create-vm-flavor-options .flavor-options.disk li").each(_.bind(function(i, el){
765
                var el_value = $(el).data("value") * 1000;
766
                if (this.unavailable_values.disk.indexOf(el_value) > -1) {
767
                    $(el).addClass("disabled");
768
                    $(el).removeClass("selected");
769
                };
770
            }, this));
771

    
772
            this.$("#create-vm-flavor-options .flavor-options.ram li").each(_.bind(function(i, el){
773
                var el_value = $(el).data("value");
774
                if (this.unavailable_values.ram.indexOf(el_value) > -1) {
775
                    $(el).addClass("disabled");
776
                    $(el).removeClass("selected");
777
                };
778
            }, this));
779

    
780
            this.$("#create-vm-flavor-options .flavor-options.cpu li").each(_.bind(function(i, el){
781
                var el_value = $(el).data("value");
782
                if (this.unavailable_values.cpu.indexOf(el_value) > -1) {
783
                    $(el).addClass("disabled");
784
                    $(el).removeClass("selected");
785
                };
786
            }, this));
787
        },
788

    
789
        create_flavors: function() {
790
            var flavors = this.get_active_flavors();
791
            var valid_flavors = this.get_valid_flavors();
792
            this.__added_flavors = {'cpu':[], 'ram':[], 'disk':[], 'disk_template':[] };
793

    
794
            _.each(flavors, _.bind(function(flv){
795
                this.add_flavor(flv);
796
            }, this));
797
            
798
            this.sort_flavors(this.disks);
799
            this.sort_flavors(this.cpus);
800
            this.sort_flavors(this.mems);
801
            this.sort_flavors(this.disk_templates);
802

    
803
            var self = this;
804
            this.$(".flavor-options li.option").click(function(){
805
                var el = $(this);
806

    
807
                if (el.hasClass("disabled")) { return }
808

    
809
                el.parent().find(".option").removeClass("selected");
810
                el.addClass("selected");
811

    
812
                if (el.hasClass("mem")) { self.last_choice = ["ram", $(this).data("value")] }
813
                if (el.hasClass("cpu")) { self.last_choice = ["cpu", $(this).data("value")] }
814
                if (el.hasClass("disk")) { self.last_choice = ["disk", $(this).data("value")] }
815
                if (el.hasClass("disk_template")) { self.last_choice = ["disk_template", $(this).data("value")] }
816

    
817
                self.update_selected_from_ui();
818
            });
819

    
820
            $(".flavor-opts-list").each(function(){
821
              var el = $(this);
822
              if (el.find(".option").length > 6) {
823
                el.addClass("compact");
824
              }
825
            });
826
        },
827

    
828
        sort_flavors: function(els) {
829
            var prev = undefined;
830
            els.find("li").each(function(i,el){
831
                el = $(el);
832
                if (!prev) { prev = el; return true };
833
                if (el.data("value") < prev.data("value")) {
834
                    prev.before(el);
835
                }
836
                prev = el;
837
            })
838
        },
839
        
840
        ui_selected: function() {
841
            var args = [this.$(".option.cpu.selected").data("value"), 
842
                this.$(".option.mem.selected").data("value"), 
843
                this.$(".option.disk.selected").data("value"),
844
                this.$(".option.disk_template.selected").data("value"),
845
            this.flavors];
846
            
847
            var flv = storage.flavors.get_flavor.apply(storage.flavors, args);
848
            return flv;
849
        },
850

    
851
        update_selected_flavor: function() {
852
            var flv = this.current_flavor;
853
            if (!flv) { return }
854
            this.$(".option").removeClass("selected");
855

    
856
            this.$(".option.cpu.value-" + flv.get("cpu")).addClass("selected");
857
            this.$(".option.mem.value-" + flv.get("ram")).addClass("selected");
858
            this.$(".option.disk.value-" + flv.get("disk")).addClass("selected");
859
            this.$(".option.disk_template.value-" + flv.get("disk_template")).addClass("selected");
860
            
861
            var disk_el = this.$(".option.disk_template.value-" + flv.get("disk_template"));
862
            var basebgpos = 470;
863
                
864
            var append_to_bg_pos = 40 + (disk_el.index() * 91);
865
            var bg_pos = basebgpos - append_to_bg_pos;
866

    
867
            this.$(".disk-template-description").css({backgroundPosition:'-' + bg_pos + 'px top'})
868
            this.$(".disk-template-description p").html(flv.get_disk_template_info().description || "");
869
        },
870
        
871
        __added_flavors: {'cpu':[], 'ram':[], 'disk':[], 'disk_template':[]},
872
        add_flavor: function(flv) {
873
            var values = {'cpu': flv.get('cpu'), 
874
                          'mem': flv.get('ram'), 
875
                          'disk': flv.get('disk'), 
876
                          'disk_template': flv.get('disk_template')};
877

    
878
            disabled = "";
879
            
880
            if (this.__added_flavors.cpu.indexOf(values.cpu) == -1) {
881
                var cpu = $(('<li class="option cpu value-{0} {1}">' + 
882
                             '<span class="value">{0}</span>' + 
883
                             '<span class="metric">x</span></li>').format(
884
                            _.escape(values.cpu), disabled)).data('value', values.cpu);
885
                this.cpus.append(cpu);
886
                this.__added_flavors.cpu.push(values.cpu);
887
            }
888

    
889
            if (this.__added_flavors.ram.indexOf(values.mem) == -1) {
890
                var mem_value = parseInt(_.escape(values.mem))*1024*1024;
891
                var displayvalue = synnefo.util.readablizeBytes(mem_value, 
892
                                                               0).split(" ");
893
                var mem = $(('<li class="option mem value-{2}">' + 
894
                             '<span class="value">{0}</span>' + 
895
                             '<span class="metric">{1}</span></li>').format(
896
                          displayvalue[0], displayvalue[1], values.mem)).data(
897
                          'value', values.mem);
898
                this.mems.append(mem);
899
                this.__added_flavors.ram.push(values.mem);
900
            }
901

    
902
            if (this.__added_flavors.disk.indexOf(values.disk) == -1) {
903
                var disk = $(('<li class="option disk value-{0}">' + 
904
                              '<span class="value">{0}</span>' + 
905
                              '<span class="metric">GB</span></li>').format(
906
                            _.escape(values.disk))).data('value', values.disk);
907
                this.disks.append(disk);
908
                this.__added_flavors.disk.push(values.disk)
909
            }
910
            
911
            if (this.__added_flavors.disk_template.indexOf(values.disk_template) == -1) {
912
                var template_info = flv.get_disk_template_info();
913
                var disk_template = $(('<li title="{2}" class="option disk_template value-{0}">' + 
914
                                       '<span class="value name">{1}</span>' +
915
                                       '</li>').format(values.disk_template, 
916
                                            _.escape(template_info.name), 
917
                                            template_info.description)).data('value', 
918
                                                                values.disk_template);
919

    
920
                this.disk_templates.append(disk_template);
921
                //disk_template.tooltip({position:'top center', offset:[-5,0], delay:100, tipClass:'tooltip disktip'});
922
                this.__added_flavors.disk_template.push(values.disk_template)
923
            }
924
            
925
        },
926
        
927
        get_active_flavors: function() {
928
            return storage.flavors.active();
929
        },
930

    
931
        get_valid_flavors: function() {
932
            return this.flavors;
933
        },
934

    
935
        update_layout: function() {
936
            this.update_selected_flavor();
937
            this.update_disabled_flavors();
938
            this.validate();
939
            this.validate_selected_flavor();
940
            this.update_quota_display();
941
        },
942
        
943
        update_quota_display: function() {
944
          if (!snf.user.quota || !snf.user.quota.data) { return };
945

    
946
          _.each(["disk", "ram", "cpu"], function(type) {
947
            var available_dsp = snf.user.quota.get_available_readable(type);
948
            var available = snf.user.quota.get_available(type);
949
            var content = "({0} left)".format(available_dsp);
950
            if (available <= 0) { content = "(None left)" }
951
            
952
            if (type == "ram") { type = "mem" }
953
            $(".flavor-options."+type+" h4 .available").text(content);
954
            if (available <= 0) {
955
              $(".flavor-options."+type+" h4 .available").addClass("error");
956
            } else {
957
              $(".flavor-options."+type+" h4 .available").removeClass("error");
958
            }
959
          })
960
        },
961

    
962
        reset: function() {
963
            this.current_image = storage.images.at(0);
964
            this.flavors = [];
965
            this.flavors_data = {'cpu':[], 'mem':[], 'disk':[]};
966
            this.update_flavors_data();
967
        },
968

    
969
        validate: function() {
970
            if (!this.current_flavor) {
971
                this.parent.$(".form-action.next").hide();
972
            } else {
973
                this.parent.$(".form-action.next").show();
974
            }
975
        },
976

    
977
        get: function() {
978
            return {'flavor': this.current_flavor}
979
        }
980

    
981
    });
982

    
983
    views.CreatePersonalizeView = views.CreateVMStepView.extend({
984
        step: 3,
985
        initialize: function() {
986
            views.CreateSubmitView.__super__.initialize.apply(this, arguments);
987
            this.roles = this.$("li.predefined-meta.role .values");
988
            this.name = this.$("input.rename-field");
989
            this.name_changed = false;
990
            this.init_suggested_roles();
991
            this.init_handlers();
992
            this.ssh_list = this.$(".ssh ul");
993
            this.selected_keys = [];
994

    
995
            var self = this;
996
            this.$(".create-ssh-key").click(function() {
997
                var confirm_close = true;
998
                if (confirm_close) {
999
                    snf.ui.main.public_keys_view.show(self.parent);
1000
                } else {
1001
                }
1002
            });
1003
        },
1004

    
1005
        init_suggested_roles: function() {
1006
            var cont = this.roles;
1007
            cont.empty();
1008
            
1009
            // TODO: get suggested from snf.api.conf
1010
            _.each(window.SUGGESTED_ROLES, function(r){
1011
                var el = $('<span class="val">{0}</span>'.format(_.escape(r)));
1012
                el.data("value", r);
1013
                cont.append(el);
1014
                el.click(function() {
1015
                    $(this).parent().find(".val").removeClass("selected");
1016
                    $(this).toggleClass("selected");
1017
                })
1018
            });
1019
            
1020
            var self = this;
1021
            $(".ssh li.ssh-key-option").live("click", function(e) {
1022
                var key = $(this).data("model");
1023
                self.select_key(key);
1024
            });
1025
        },
1026

    
1027
        select_key: function(key) {
1028
            var exists = this.selected_keys.indexOf(key.id);
1029
            if (exists > -1) {
1030
                this.selected_keys.splice(exists, 1);
1031
            } else {
1032
                this.selected_keys.push(key.id);
1033
            }
1034
            this.update_ui_keys_selections(this.selected_keys);
1035
        },
1036

    
1037
        update_ui_keys_selections: function(keys) {
1038
            var self = this;
1039
            self.$(".ssh-key-option").removeClass("selected");
1040
            self.$(".ssh-key-option .check").attr("checked", false);
1041
            _.each(keys, function(kid) {
1042
                $("#ssh-key-option-" + kid).addClass("selected");
1043
                $("#ssh-key-option-" + kid).find(".check").attr("checked", true);
1044
            });
1045
        },
1046

    
1047
        update_ssh_keys: function() {
1048
            this.ssh_list.empty();
1049
            var keys = snf.storage.keys.models;
1050
            if (keys.length == 0) { 
1051
                this.$(".ssh .empty").show();
1052
            } else {
1053
                this.$(".ssh .empty").hide();
1054
            }
1055
            _.each(keys, _.bind(function(key){
1056
                var el = $('<li id="ssh-key-option-{1}" class="ssh-key-option">{0}</li>'.format(_.escape(key.get("name")), key.id));
1057
                var check = $('<input class="check" type="checkbox"></input>')
1058
                el.append(check);
1059
                el.data("model", key);
1060
                this.ssh_list.append(el);
1061
            }, this));
1062
        },
1063

    
1064
        init_handlers: function() {
1065
            this.name.bind("keypress", _.bind(function(e) {
1066
                this.name_changed = true;
1067
                if (e.keyCode == 13) { this.parent.set_step(4); this.parent.update_layout() };    
1068
            }, this));
1069

    
1070
            this.name.bind("click", _.bind(function() {
1071
                if (!this.name_changed) {
1072
                    this.name.val("");
1073
                }
1074
            }, this))
1075
        },
1076

    
1077
        show: function() {
1078
            views.CreatePersonalizeView.__super__.show.apply(this, arguments);
1079
            this.update_layout();
1080
        },
1081
        
1082
        update_layout: function() {
1083
            var params = this.parent.get_params();
1084

    
1085
            if (!params.image || !params.flavor) { return }
1086

    
1087
            if (!params.image) { return }
1088
            var vm_name_tpl = snf.config.vm_name_template || "My {0} server";
1089
            var vm_name = vm_name_tpl.format(_.escape(params.image.get("name")));
1090
            var orig_name = vm_name;
1091
            
1092
            var existing = true;
1093
            var j = 0;
1094

    
1095
            while (existing && !this.name_changed) {
1096
                var existing = storage.vms.select(function(vm){return vm.get("name") == vm_name}).length
1097
                if (existing) {
1098
                    j++;
1099
                    vm_name = orig_name + " " + j;
1100
                }
1101
            }
1102

    
1103
            if (!_(this.name.val()).trim() || !this.name_changed) {
1104
                this.name.val(vm_name);
1105
            }
1106

    
1107
            if (!this.name_changed && this.parent.visible()) {
1108
                if (!$.browser.msie && !$.browser.opera) {
1109
                    this.$("#create-vm-name").select();
1110
                } else {
1111
                    window.setTimeout(_.bind(function(){
1112
                        this.$("#create-vm-name").select();
1113
                    }, this), 400)
1114
                }
1115
            }
1116
            
1117
            var img = snf.ui.helpers.os_icon_path(params.image.get("OS"))
1118
            this.name.css({backgroundImage:"url({0})".format(img)})
1119
            
1120
            if (!params.image.supports('ssh')) {
1121
                this.disable_ssh_keys();
1122
            } else {
1123
                this.enable_ssh_keys();
1124
                this.update_ssh_keys();
1125
            }
1126

    
1127
            this.update_ui_keys_selections(this.selected_keys);
1128
        },
1129

    
1130
        disable_ssh_keys: function() {
1131
            this.$(".disabled.desc").show();
1132
            this.$(".empty.desc").hide();
1133
            this.$(".ssh .confirm-params").hide();
1134
            this.selected_keys = [];
1135
        },
1136

    
1137
        enable_ssh_keys: function() {
1138
            this.$(".ssh .confirm-params").show();
1139
            this.$(".disabled.desc").hide();
1140
        },
1141

    
1142
        reset: function() {
1143
            this.roles.find(".val").removeClass("selected");
1144
            this.name_changed = false;
1145
            this.selected_keys = [];
1146
            this.update_layout();
1147
        },
1148

    
1149
        get_meta: function() {
1150
            if (this.roles.find(".selected").length == 0) {
1151
                return false;
1152
            }
1153

    
1154
            var role = $(this.roles.find(".selected").get(0)).data("value");
1155
            return {'Role': role }
1156
        },
1157

    
1158
        get: function() {
1159
            var val = {'name': this.name.val() };
1160
            if (this.get_meta()) {
1161
                val.metadata = this.get_meta();
1162
            }
1163

    
1164
            val.keys = _.map(this.selected_keys, function(k){ return snf.storage.keys.get(k)});
1165
            
1166
            return val;
1167
        }
1168
    });
1169

    
1170
    views.CreateSubmitView = views.CreateVMStepView.extend({
1171
        step: 4,
1172
        initialize: function() {
1173
            views.CreateSubmitView.__super__.initialize.apply(this, arguments);
1174
            this.roles = this.$("li.predefined-meta.role .values");
1175
            this.confirm = this.$(".confirm-params ul");
1176
            this.name = this.$("h3.vm-name");
1177
            this.keys = this.$(".confirm-params.ssh");
1178
            this.meta = this.$(".confirm-params.meta");
1179
            this.init_handlers();
1180
        },
1181

    
1182
        init_handlers: function() {
1183
        },
1184

    
1185
        show: function() {
1186
            views.CreateSubmitView.__super__.show.apply(this, arguments);
1187
            this.update_layout();
1188
        },
1189
        
1190
        update_flavor_details: function() {
1191
            var flavor = this.parent.get_params().flavor;
1192

    
1193
            function set_detail(sel, key) {
1194
                var val = key;
1195
                if (key == undefined) { val = flavor.get(sel) };
1196
                this.$(".confirm-cont.flavor .flavor-" + sel + " .value").text(val)
1197
            }
1198
            
1199
            set_detail("cpu", flavor.get("cpu") + "x");
1200
            set_detail("ram", flavor.get("ram") + " MB");
1201
            set_detail("disk", util.readablizeBytes(flavor.get("disk") * 1024 * 1024 * 1024));
1202
            set_detail("disktype", flavor.get_disk_template_info().name);
1203
        },
1204

    
1205
        update_image_details: function() {
1206
            var image = this.parent.get_params().image;
1207

    
1208
            function set_detail(sel, key) {
1209
                var val = key;
1210
                if (key == undefined) { val = image.get(sel) };
1211
                this.$(".confirm-cont.image .image-" + sel + " .value").text(val)
1212
            }
1213
            
1214
            set_detail("description", image.get_description());
1215
            set_detail("name");
1216
            set_detail("os", _(image.get_os()).capitalize());
1217
            set_detail("gui", image.get_gui());
1218
            set_detail("size", _.escape(image.get_readable_size()));
1219
            set_detail("kernel");
1220
        },
1221

    
1222
        update_selected_keys: function(keys) {
1223
            this.keys.empty();
1224
            if (!keys || keys.length == 0) {
1225
                this.keys.append(this.make("li", {'class':'empty'}, 'No keys selected'))
1226
            }
1227
            _.each(keys, _.bind(function(key) {
1228
                var el = this.make("li", {'class':'selected-ssh-key'}, key.get('name'));
1229
                this.keys.append(el);
1230
            }, this))
1231
        },
1232

    
1233
        update_selected_meta: function(meta) {
1234
            this.meta.empty();
1235
            if (!meta || meta.length == 0) {
1236
                this.meta.append(this.make("li", {'class':'empty'}, 'No tags selected'))
1237
            }
1238
            _.each(meta, _.bind(function(value, key) {
1239
                var el = this.make("li", {'class':"confirm-value"});
1240
                var name = this.make("span", {'class':"ckey"}, key);
1241
                var value = this.make("span", {'class':"cval"}, value);
1242

    
1243
                $(el).append(name)
1244
                $(el).append(value);
1245
                this.meta.append(el);
1246
            }, this));
1247
        },
1248

    
1249
        update_layout: function() {
1250
            var params = this.parent.get_params();
1251
            if (!params.image || !params.flavor) { return }
1252

    
1253
            if (!params.image) { return }
1254

    
1255
            this.name.text(params.name);
1256

    
1257
            this.confirm.find("li.image .value").text(params.flavor.get("image"));
1258
            this.confirm.find("li.cpu .value").text(params.flavor.get("cpu") + "x");
1259
            this.confirm.find("li.mem .value").text(params.flavor.get("ram"));
1260
            this.confirm.find("li.disk .value").text(params.flavor.get("disk"));
1261

    
1262
            var img = snf.ui.helpers.os_icon_path(params.image.get("OS"))
1263
            this.name.css({backgroundImage:"url({0})".format(img)})
1264

    
1265
            this.update_image_details();
1266
            this.update_flavor_details();
1267

    
1268
            if (!params.image.supports('ssh')) {
1269
                this.keys.hide();
1270
                this.keys.prev().hide();
1271
            } else {
1272
                this.keys.show();
1273
                this.keys.prev().show();
1274
                this.update_selected_keys(params.keys);
1275
            }
1276
            
1277
            this.update_selected_meta(params.metadata);
1278
        },
1279

    
1280
        reset: function() {
1281
            this.update_layout();
1282
        },
1283

    
1284
        get_meta: function() {
1285
        },
1286

    
1287
        get: function() {
1288
            return {};
1289
        }
1290
    });
1291

    
1292
    views.CreateVMView = views.Overlay.extend({
1293
        
1294
        view_id: "create_vm_view",
1295
        content_selector: "#createvm-overlay-content",
1296
        css_class: 'overlay-createvm overlay-info',
1297
        overlay_id: "metadata-overlay",
1298

    
1299
        subtitle: false,
1300
        title: "Create new machine",
1301

    
1302
        initialize: function(options) {
1303
            views.CreateVMView.__super__.initialize.apply(this);
1304
            this.current_step = 1;
1305

    
1306
            this.password_view = new views.VMCreationPasswordView();
1307

    
1308
            this.steps = [];
1309
            this.steps[1] = new views.CreateImageSelectView(this);
1310
            this.steps[1].bind("change", _.bind(function(data) {this.trigger("image:change", data)}, this));
1311

    
1312
            this.steps[2] = new views.CreateFlavorSelectView(this);
1313
            this.steps[3] = new views.CreatePersonalizeView(this);
1314
            this.steps[4] = new views.CreateSubmitView(this);
1315

    
1316
            this.cancel_btn = this.$(".create-controls .cancel");
1317
            this.next_btn = this.$(".create-controls .next");
1318
            this.prev_btn = this.$(".create-controls .prev");
1319
            this.submit_btn = this.$(".create-controls .submit");
1320

    
1321
            this.history = this.$(".steps-history");
1322
            this.history_steps = this.$(".steps-history .steps-history-step");
1323
            
1324
            this.init_handlers();
1325
        },
1326

    
1327
        init_handlers: function() {
1328
            var self = this;
1329
            this.next_btn.click(_.bind(function(){
1330
                this.set_step(this.current_step + 1);
1331
                this.update_layout();
1332
            }, this))
1333
            this.prev_btn.click(_.bind(function(){
1334
                this.set_step(this.current_step - 1);
1335
                this.update_layout();
1336
            }, this))
1337
            this.cancel_btn.click(_.bind(function(){
1338
                this.close_all();
1339
            }, this))
1340
            this.submit_btn.click(_.bind(function(){
1341
                this.submit();
1342
            }, this))
1343
            
1344
            this.history.find(".completed").live("click", function() {
1345
                var step = parseInt($(this).attr("id").replace("vm-create-step-history-", ""));
1346
                self.set_step(step);
1347
                self.update_layout();
1348
            })
1349
        },
1350

    
1351
        set_step: function(st) {
1352
        },
1353
        
1354
        validate: function(data) {
1355
            if (_(data.name).trim() == "") {
1356
                this.$(".form-field").addClass("error");
1357
                return false;
1358
            } else {
1359
                return true;
1360
            }
1361
        },
1362

    
1363
        submit: function() {
1364
            if (this.submiting) { return };
1365
            var data = this.get_params();
1366
            var meta = {};
1367
            var extra = {};
1368
            var personality = [];
1369

    
1370
            if (this.validate(data)) {
1371
                this.submit_btn.addClass("in-progress");
1372
                this.submiting = true;
1373
                if (data.metadata) { meta = data.metadata; }
1374
                if (data.keys && data.keys.length > 0) {
1375
                    personality.push(data.image.personality_data_for_keys(data.keys))
1376
                }
1377

    
1378
                if (personality.length) {
1379
                    extra['personality'] = _.flatten(personality);
1380
                }
1381

    
1382
                storage.vms.create(data.name, data.image, data.flavor, meta, extra, _.bind(function(data){
1383
                    this.close_all();
1384
                    this.password_view.show(data.server.adminPass, data.server.id);
1385
                    this.submiting = false;
1386
                }, this));
1387
            }
1388
        },
1389

    
1390
        close_all: function() {
1391
            this.hide();
1392
        },
1393

    
1394
        reset: function() {
1395
            this.current_step = 1;
1396

    
1397
            this.steps[1].reset();
1398
            this.steps[2].reset();
1399
            this.steps[3].reset();
1400
            this.steps[4].reset();
1401

    
1402
            this.steps[1].show();
1403
            this.steps[2].show();
1404
            this.steps[3].show();
1405
            this.steps[4].show();
1406

    
1407
            this.submit_btn.removeClass("in-progress");
1408
        },
1409

    
1410
        onShow: function() {
1411
        },
1412

    
1413
        update_layout: function() {
1414
            this.show_step(this.current_step);
1415
            this.current_view.update_layout();
1416
        },
1417

    
1418
        beforeOpen: function() {
1419
            if (!this.skip_reset_on_next_open) {
1420
                this.submiting = false;
1421
                this.reset();
1422
                this.current_step = 1;
1423
                this.$(".steps-container").css({"margin-left":0 + "px"});
1424
                this.show_step(1);
1425
            }
1426
            
1427
            this.skip_reset_on_next_open = false;
1428
            this.update_layout();
1429
        },
1430
        
1431
        set_step: function(step) {
1432
            if (step <= 1) {
1433
                step = 1
1434
            }
1435
            if (step > this.steps.length - 1) {
1436
                step = this.steps.length - 1;
1437
            }
1438
            this.current_step = step;
1439
        },
1440

    
1441
        show_step: function(step) {
1442
            // FIXME: this shouldn't be here
1443
            // but since we are not calling step.hide this should work
1444
            this.steps[1].image_details.hide();
1445

    
1446
            this.current_view = this.steps[step];
1447
            this.update_controls();
1448

    
1449
            this.steps[step].show();
1450
            var width = this.el.find('.container').width();
1451
            var left = (step -1) * width * -1;
1452
            this.$(".steps-container").animate({"margin-left": left + "px"}, 300);
1453

    
1454
            this.update_steps_history();
1455
        },
1456

    
1457
        update_steps_history: function() {
1458
            var self = this;
1459
            function get_step(s) {
1460
                return self.history.find(".step" + s + "h");
1461
            }
1462
            
1463
            var current_step = parseInt(this.current_view.step);
1464
            _.each(this.steps, function(stepv) {
1465
                var step = parseInt(stepv.step);
1466
                get_step(step).removeClass("completed").removeClass("current");
1467
                if (step == current_step) {
1468
                    get_step(step).removeClass("completed").addClass("current");
1469
                }
1470
                if (step < current_step) {
1471
                    get_step(step).removeClass("current").addClass("completed");
1472
                }
1473
            });
1474
        },
1475

    
1476
        update_controls: function() {
1477
            var step = this.current_step;
1478
            if (step == 1) {
1479
                this.prev_btn.hide();
1480
                this.cancel_btn.show();
1481
            } else {
1482
                this.prev_btn.show();
1483
                this.cancel_btn.hide();
1484
            }
1485
            
1486
            if (step == this.steps.length - 1) {
1487
                this.next_btn.hide();
1488
                this.submit_btn.show();
1489
            } else {
1490
                this.next_btn.show();
1491
                this.submit_btn.hide();
1492
            }
1493
        },
1494

    
1495
        get_params: function() {
1496
            return _.extend({}, this.steps[1].get(), this.steps[2].get(), this.steps[3].get());
1497
        }
1498
    });
1499
    
1500
})(this);
1501