Statistics
| Branch: | Tag: | Revision:

root / synnefo / ui / static / snf / js / ui / web / ui_create_view.js @ c64333b6

History | View | Annotate | Download (42.2 kB)

1
;(function(root){
2

    
3
    // root
4
    var root = root;
5
    
6
    // setup namepsaces
7
    var snf = root.synnefo = root.synnefo || {};
8
    var models = snf.models = snf.models || {}
9
    var storage = snf.storage = snf.storage || {};
10
    var ui = snf.ui = snf.ui || {};
11
    var util = snf.util = snf.util || {};
12

    
13
    var views = snf.views = snf.views || {}
14

    
15
    // shortcuts
16
    var bb = root.Backbone;
17

    
18

    
19
    views.VMCreationPasswordView = views.Overlay.extend({
20
        view_id: "creation_password_view",
21
        content_selector: "#creation-password-overlay",
22
        css_class: 'overlay-password overlay-info',
23
        overlay_id: "creation-password-overlay",
24

    
25
        subtitle: "",
26
        title: "Machine password",
27

    
28
        initialize: function(options) {
29
            views.FeedbackView.__super__.initialize.apply(this, arguments);
30
            _.bindAll(this, 'show_password');
31

    
32
            this.password = this.$("#new-machine-password");
33
            this.copy = this.$(".clipboard");
34

    
35
            this.$(".show-machine").click(_.bind(function(){
36
                if (this.$(".show-machine").hasClass("in-progress")) {
37
                    return;
38
                }
39
                this.hide();
40
                snf.ui.main.show_vm_details(storage.vms.get(this.vm_id));
41
            }, this));
42

    
43
            _.bindAll(this, "handle_vm_added");
44
            storage.vms.bind("add", this.handle_vm_added);
45
            this.password.text("");
46
        },
47

    
48
        handle_vm_added: function() {
49
            this.$(".show-machine").removeClass("in-progress");
50
        },
51
        
52
        show_password: function() {
53
            this.$(".show-machine").addClass("in-progress");
54
            this.password.text(this.pass);
55
            if (storage.vms.get(this.vm_id)) {
56
                this.$(".show-machine").removeClass("in-progress");
57
            }
58
            
59
            this.clip = new snf.util.ClipHelper(this.copy, this.pass);
60
        },
61

    
62
        onClose: function() {
63
            this.password.text("");
64
            this.vm_id = undefined;
65
            try { delete this.clip; } catch (err) {};
66
        },
67
        
68
        beforeOpen: function() {
69
            this.copy.empty();
70
        },
71
        
72
        onOpen: function() {
73
            this.show_password();
74
        },
75

    
76
        show: function(pass, vm_id) {
77
            this.pass = pass;
78
            this.vm_id = vm_id;
79
            
80
            views.VMCreationPasswordView.__super__.show.apply(this, arguments);
81
        }
82
    })
83

    
84

    
85
    
86
    views.CreateVMStepView = views.View.extend({
87
        step: "1",
88
        title: "Image",
89
        submit: false,
90

    
91
        initialize: function(view) {
92
            this.parent = view;
93
            this.el = view.$("div.create-step-cont.step-" + this.step);
94
            this.header = this.$(".step-header .step-" + this.step);
95
            this.view_id = "create_step_" + this.step;
96

    
97
            views.CreateVMStepView.__super__.initialize.apply(this);
98
        },
99

    
100
        show: function() {
101
            // show current
102
            this.el.show();
103
            this.header.addClass("current");
104
            this.header.show();
105
            this.update_layout();
106
        },
107

    
108
        reset: function() {
109
        }
110
    })
111

    
112
    views.CreateImageSelectView = views.CreateVMStepView.extend({
113

    
114
        initialize: function() {
115
            views.CreateImageSelectView.__super__.initialize.apply(this, arguments);
116

    
117
            // elements
118
            this.images_list_cont = this.$(".images-list-cont");
119
            this.images_list = this.$(".images-list-cont ul");
120
            this.image_details = this.$(".images-info-cont");
121
            this.image_details_desc = this.$(".images-info-cont .description p");
122
            this.image_details_title = this.$(".images-info-cont h4");
123
            this.image_details_size = this.$(".images-info-cont .size p");
124
            this.image_details_os = this.$(".images-info-cont .os p");
125
            this.image_details_kernel = this.$(".images-info-cont .kernel p");
126
            this.image_details_gui = this.$(".images-info-cont .gui p");
127

    
128
            this.types = this.$(".type-filter li");
129
            this.categories_list = this.$(".category-filters");
130

    
131
            // params initialization
132
            this.type_selections = ["system", "custom"]
133
            this.selected_type = "system";
134
            this.selected_categories = [];
135
            this.images = [];
136

    
137
            // update
138
            this.update_images();
139

    
140
            // handlers initialization
141
            this.init_handlers();
142
            this.init_position();
143
        },
144

    
145
        init_position: function() {
146
            //this.el.css({position: "absolute"});
147
            //this.el.css({top:"10px"})
148
        },
149
        
150
        init_handlers: function() {
151
            var self = this;
152
            this.types.live("click", function() {
153
                self.select_type($(this).attr("id").replace("type-select-",""));
154
            })
155
        },
156

    
157
        update_images: function() {
158
            this.images = storage.images.active();
159
            this.images_ids = _.map(this.images, function(img){return img.id});
160
            if (this.selected_type == "custom") { this.images = []; this.images_ids = []; }
161

    
162
            return this.images;
163
        },
164

    
165
        update_layout: function() {
166
            this.select_type(this.selected_type);
167
        },
168
        
169
        get_categories: function(images) {
170
            return [];
171
            return ["Desktop", "Server", "Linux", "Windows"];
172
        },
173

    
174
        reset_categories: function() {
175
            var categories = this.get_categories(this.images);
176
            this.categories_list.find("li").remove();
177

    
178
            _.each(categories, _.bind(function(cat) {
179
                var el = $("<li />");
180
                el.text(cat);
181
                this.categories_list.append(el);
182
            }, this));
183

    
184
            if (!categories.length) { 
185
                this.categories_list.parent().find(".clear").hide();
186
                this.categories_list.parent().find(".empty").show();
187
            } else {
188
                this.categories_list.parent().find(".clear").show();
189
                this.categories_list.parent().find(".empty").hide();
190
            }
191
        },
192
        
193
        select_type: function(type) {
194
            this.selected_type = type;
195
            this.types.removeClass("selected");
196
            this.types.filter("#type-select-" + this.selected_type).addClass("selected");
197

    
198
            this.reset_categories();
199
            this.update_images();
200
            this.reset_images();
201
            this.select_image();
202
        },
203

    
204
        select_image: function(image) {
205
            if (!image && this.images_ids.length) {
206
                if (this.selected_image && this.images_ids.indexOf(this.selected_image.id) > -1) {
207
                    image = this.selected_image;
208
                } else {
209
                    image = storage.images.get(this.images_ids[0]);
210
                }
211
            }
212

    
213
            if (!this.images_ids.length) { image = this.selected_image || undefined };
214
            
215
            this.selected_image = image;
216
            this.trigger("change", image);
217
            
218
            if (image) {
219
                this.image_details.show();
220
                this.images_list.find(".image-details").removeClass("selected");
221
                this.images_list.find(".image-details#create-vm-image-" + this.selected_image.id).addClass("selected");
222
                
223
                this.image_details_desc.text(image.get("description"));
224
                
225
                var img = snf.ui.helpers.os_icon_tag(image.get("OS"))
226
                this.image_details_title.html(img + image.get("name"));
227
                this.image_details_os.text(_(image.get("OS")).capitalize());
228
                this.image_details_kernel.text(image.get("kernel"));
229

    
230
                var size = image.get_readable_size();
231

    
232
                this.image_details_size.text(size);
233
                this.image_details_gui.text(image.get("GUI"));
234

    
235
            } else {
236
                this.image_details.hide();
237
            }
238

    
239
            this.validate();
240
        },
241

    
242
        reset_images: function() {
243
            this.images_list.find("li").remove();
244
            _.each(this.images, _.bind(function(img){
245
                this.add_image(img);
246
            }, this))
247
            
248
            if (this.images.length) {
249
                this.images_list.parent().find(".empty").hide();
250
            } else {
251
                this.images_list.parent().find(".empty").show();
252
            }
253

    
254
            this.select_image();
255
            
256
            var self = this;
257
            this.images_list.find(".image-details").click(function(){
258
                self.select_image($(this).data("image"));
259
            });
260
            
261
        },
262

    
263
        show: function() {
264
            views.CreateImageSelectView.__super__.show.apply(this, arguments);
265
        },
266

    
267
        add_image: function(img) {
268
            var image = $(('<li id="create-vm-image-{1}"' +
269
                           'class="image-details clearfix">{2}{0}' +
270
                           '<p>{4}</p><span class="size">{3}' +
271
                           '</span></li>').format(img.get("name"), 
272
                                                  img.id, 
273
                                                  snf.ui.helpers.os_icon_tag(img.get("OS")),
274
                                                  img.get_readable_size(),
275
                                                  util.truncate(img.get("description"), 35)));
276
            image.data("image", img);
277
            image.data("image_id", img.id);
278
            this.images_list.append(image);
279
        },
280

    
281
        reset: function() {
282
            this.selected_image = undefined;
283
            this.reset_images();
284
        },
285

    
286
        get: function() {
287
            return {'image': this.selected_image};
288
        },
289

    
290
        validate: function() {
291
            if (!this.selected_image) {
292
                this.parent.$(".form-action.next").hide();
293
            } else {
294
                this.parent.$(".form-action.next").show();
295
            }
296
        }
297
    });
298

    
299
    views.CreateFlavorSelectView = views.CreateVMStepView.extend({
300
        step: 2,
301
        initialize: function() {
302
            views.CreateFlavorSelectView.__super__.initialize.apply(this, arguments);
303
            this.parent.bind("image:change", _.bind(this.handle_image_change, this));
304

    
305
            this.cpus = this.$(".flavors-cpu-list");
306
            this.disks = this.$(".flavors-disk-list");
307
            this.disk_templates = this.$(".flavors-disk-template-list");
308
            this.mems = this.$(".flavors-mem-list");
309

    
310
            this.predefined_flavors = SUGGESTED_FLAVORS;
311
            this.predefined_flavors_keys = _.keys(SUGGESTED_FLAVORS);
312
            this.predefined_flavors_keys = _.sortBy(this.predefined_flavors_keys, _.bind(function(k){
313
                var flv = this.predefined_flavors[k];
314
                return (flv.ram * flv.cpu * flv.disk) + flv.disk_template;
315
            }, this));
316

    
317
            this.predefined = this.$(".predefined-list");
318
            this.update_predefined_flavors();
319
        },
320

    
321
        handle_image_change: function(data) {
322
            this.current_image = data;
323
            this.update_valid_predefined();
324
            this.update_flavors_data();
325
            this.reset_flavors();
326
            this.update_layout();
327
        },
328

    
329
        validate_selected_flavor: function() {
330
            if (!this.flavor_is_valid(this.current_flavor)) {
331
                this.select_valid_flavor();
332
            }
333
        },
334

    
335
        reset_flavors: function() {
336
            this.$(".flavor-opts-list .option").remove();
337
            this.create_flavors();
338
        },
339

    
340
        update_predefined_flavors: function() {
341
            this.predefined.find("li").remove();
342
            _.each(this.predefined_flavors_keys, _.bind(function(key) {
343
                var val = this.predefined_flavors[key];
344
                var el = $(('<li class="predefined-selection" id="predefined-flavor-{0}">' +
345
                           '{1}</li>').format(key, _(key).capitalize()));
346

    
347
                this.predefined.append(el);
348
                el.data({flavor: storage.flavors.get_flavor(val.cpu, val.ram, val.disk, val.disk_template, this.flavors)});
349
                el.click(_.bind(function() {
350
                    this.handle_predefined_click(el);
351
                }, this))
352
            }, this));
353
            this.update_valid_predefined();
354
        },
355

    
356
        handle_predefined_click: function(el) {
357
            if (el.hasClass("disabled")) { return };
358
            this.set_current(el.data("flavor"));
359
        },
360

    
361
        select_valid_flavor: function() {
362
            var found = false;
363
            var self = this;
364
            _.each(this.flavors, function(flv) {
365
                if (self.flavor_is_valid(flv)) {
366
                    found = flv;
367
                    return false;
368
                }
369
            });
370
            
371
            if (found) {
372
                this.set_current(found);
373
            } else {
374
                this.current_flavor = undefined;
375
                this.validate();
376
                this.$("li.predefined-selection").addClass("disabled");
377
                this.$(".flavor-opts-list li").removeClass("selected");
378
            }
379
        },
380

    
381
        update_valid_predefined: function() {
382
            this.update_unavailable_values();
383
            var self = this;
384
            this.valid_predefined = _.select(_.map(this.predefined_flavors, function(flv, key){
385
                var existing = storage.flavors.get_flavor(flv.cpu, flv.ram, flv.disk, flv.disk_template, self.flavors);
386
                // non existing
387
                if (!existing) {
388
                    return false;
389
                }
390
                
391
                // not available for image
392
                if (self.unavailable_values && self.unavailable_values.disk.indexOf(existing.get_disk_size()) > -1) {
393
                    return false
394
                }
395

    
396
                return key;
397
            }), function(ret) { return ret });
398
            
399
            $("li.predefined-selection").addClass("disabled");
400
            _.each(this.valid_predefined, function(key) {
401
                $("#predefined-flavor-" + key).removeClass("disabled");
402
            })
403
        },
404

    
405
        update_selected_predefined: function() {
406
            var self = this;
407
            this.predefined.find("li").removeClass("selected");
408

    
409
            _.each(this.valid_predefined, function(key){
410
                var flv = self.predefined_flavors[key];
411
                var exists = storage.flavors.get_flavor(flv.cpu, flv.ram, flv.disk, flv.disk_template, self.flavors);
412

    
413
                if (exists && (exists.id == self.current_flavor.id)) {
414
                    $("#predefined-flavor-" + key).addClass("selected");
415
                }
416
            })
417
        },
418
        
419
        update_flavors_data: function() {
420
            this.flavors = storage.flavors.active();
421
            this.flavors_data = storage.flavors.get_data(this.flavors);
422
            
423
            var self = this;
424
            var set = false;
425
            
426
            // FIXME: validate current flavor
427
            
428
            if (!this.current_flavor) {
429
                _.each(this.valid_predefined, function(key) {
430
                    var flv = self.predefined_flavors[key];
431
                    var exists = storage.flavors.get_flavor(flv.cpu, flv.ram, flv.disk, flv.disk_template, self.flavors);
432
                    if (exists && !set) {
433
                        self.set_current(exists);
434
                        set = true;
435
                    }
436
                })
437
            }
438

    
439
            this.update_unavailable_values();
440
        },
441

    
442
        update_unavailable_values: function() {
443
            if (!this.current_image) { this.unavailable_values = {disk:[], ram:[], cpu:[]}; return };
444
            this.unavailable_values = storage.flavors.unavailable_values_for_image(this.current_image);
445
        },
446
        
447
        flavor_is_valid: function(flv) {
448
            if (!flv) { return false };
449

    
450
            var existing = storage.flavors.get_flavor(flv.get("cpu"), flv.get("ram"), flv.get("disk"), flv.get("disk_template"), this.flavors);
451
            if (!existing) { return false };
452
            
453
            if (this.unavailable_values && (this.unavailable_values.disk.indexOf(parseInt(flv.get("disk")) * 1000) > -1)) {
454
                return false;
455
            }
456
            return true;
457
        },
458
            
459
        set_valid_current_for: function(t, val) {
460
            var found = this.flavors[0];
461
            _.each(this.flavors, function(flv) {
462
                if (flv.get(t) == val) {
463
                    found = flv;
464
                }
465
            });
466

    
467
            this.set_current(found);
468
            this.validate_selected_flavor();
469
        },
470

    
471
        set_current: function(flv) {
472

    
473
            if (!flv) {
474
                // user clicked on invalid combination
475
                // force the first available choice for the
476
                // type of option he last clicked
477
                this.set_valid_current_for.apply(this, this.last_choice);
478
                return;
479
            }
480

    
481
            this.current_flavor = flv;
482
            this.trigger("change");
483
            if (this.current_flavor) {
484
                this.update_selected_flavor();
485
                this.update_selected_predefined();
486
            }
487
            
488
            this.validate();
489
        },
490
        
491
        select_default_flavor: function() {
492
               
493
        },
494

    
495
        update_selected_from_ui: function() {
496
            this.set_current(this.ui_selected());
497
        },
498
        
499
        update_disabled_flavors: function() {
500
            this.$(".flavor-options.disk li").removeClass("disabled");
501
            if (!this.unavailable_values) { return }
502
            
503
            this.$("#create-vm-flavor-options .flavor-options.disk li").each(_.bind(function(i, el){
504
                var el_value = $(el).data("value") * 1000;
505
                if (this.unavailable_values.disk.indexOf(el_value) > -1) {
506
                    $(el).addClass("disabled");
507
                };
508
            }, this));
509
        },
510

    
511
        create_flavors: function() {
512
            var flavors = this.get_active_flavors();
513
            var valid_flavors = this.get_valid_flavors();
514
            this.__added_flavors = {'cpu':[], 'ram':[], 'disk':[], 'disk_template':[] };
515

    
516
            _.each(flavors, _.bind(function(flv){
517
                this.add_flavor(flv);
518
            }, this));
519
            
520
            this.sort_flavors(this.disks);
521
            this.sort_flavors(this.cpus);
522
            this.sort_flavors(this.mems);
523
            this.sort_flavors(this.disk_templates);
524

    
525
            var self = this;
526
            this.$(".flavor-options li.option").click(function(){
527
                var el = $(this);
528

    
529
                if (el.hasClass("disabled")) { return }
530

    
531
                el.parent().find(".option").removeClass("selected");
532
                el.addClass("selected");
533
                
534
                if (el.hasClass("mem")) { self.last_choice = ["ram", $(this).data("value")] }
535
                if (el.hasClass("cpu")) { self.last_choice = ["cpu", $(this).data("value")] }
536
                if (el.hasClass("disk")) { self.last_choice = ["disk", $(this).data("value")] }
537
                if (el.hasClass("disk_template")) { self.last_choice = ["disk_template", $(this).data("value")] }
538

    
539
                self.update_selected_from_ui();
540
            })
541

    
542
            //this.$(".flavor-options li.disk_template.option").mouseover(function(){
543
                //$(this).parent().find(".description").hide();
544
                //$(this).find(".description").show();
545
            //}).mouseout(function(){
546
                //$(this).parent().find(".description").hide();
547
                //$(this).parent().find(".selected .description").show();
548
            //});
549
        },
550

    
551
        sort_flavors: function(els) {
552
            var prev = undefined;
553
            els.find("li").each(function(i,el){
554
                el = $(el);
555
                if (!prev) { prev = el; return true };
556
                if (el.data("value") < prev.data("value")) {
557
                    prev.before(el);
558
                }
559
                prev = el;
560
            })
561
        },
562
        
563
        ui_selected: function() {
564
            var args = [this.$(".option.cpu.selected").data("value"), 
565
                this.$(".option.mem.selected").data("value"), 
566
                this.$(".option.disk.selected").data("value"),
567
                this.$(".option.disk_template.selected").data("value"),
568
            this.flavors];
569

    
570
            var flv = storage.flavors.get_flavor.apply(storage.flavors, args);
571
            return flv;
572
        },
573

    
574
        update_selected_flavor: function() {
575
            var flv = this.current_flavor;
576
            if (!flv) { return }
577
            this.$(".option").removeClass("selected");
578

    
579
            this.$(".option.cpu.value-" + flv.get("cpu")).addClass("selected");
580
            this.$(".option.mem.value-" + flv.get("ram")).addClass("selected");
581
            this.$(".option.disk.value-" + flv.get("disk")).addClass("selected");
582
            this.$(".option.disk_template.value-" + flv.get("disk_template")).addClass("selected");
583
            
584
            var disk_el = this.$(".option.disk_template.value-" + flv.get("disk_template"));
585
            var basebgpos = 470;
586
                
587
            var append_to_bg_pos = 40 + (disk_el.index() * 91);
588
            var bg_pos = basebgpos - append_to_bg_pos;
589

    
590
            this.$(".disk-template-description").css({backgroundPosition:'-' + bg_pos + 'px top'})
591
            this.$(".disk-template-description p").html(flv.get_disk_template_info().description || "");
592
        },
593
        
594
        __added_flavors: {'cpu':[], 'ram':[], 'disk':[], 'disk_template':[]},
595
        add_flavor: function(flv) {
596
            var values = {'cpu': flv.get('cpu'), 
597
                          'mem': flv.get('ram'), 
598
                          'disk': flv.get('disk'), 
599
                          'disk_template': flv.get('disk_template')};
600

    
601
            disabled = "";
602
            
603
            if (this.__added_flavors.cpu.indexOf(values.cpu) == -1) {
604
                var cpu = $(('<li class="option cpu value-{0} {1}">' + 
605
                             '<span class="value">{0}</span>' + 
606
                             '<span class="metric">x</span></li>').format(values.cpu, disabled)).data('value', values.cpu);
607
                this.cpus.append(cpu);
608
                this.__added_flavors.cpu.push(values.cpu);
609
            }
610

    
611
            if (this.__added_flavors.ram.indexOf(values.mem) == -1) {
612
                var mem = $(('<li class="option mem value-{0}">' + 
613
                             '<span class="value">{0}</span>' + 
614
                             '<span class="metric">MB</span></li>').format(values.mem)).data('value', values.mem);
615
                this.mems.append(mem);
616
                this.__added_flavors.ram.push(values.mem);
617
            }
618

    
619
            if (this.__added_flavors.disk.indexOf(values.disk) == -1) {
620
                var disk = $(('<li class="option disk value-{0}">' + 
621
                              '<span class="value">{0}</span>' + 
622
                              '<span class="metric">GB</span></li>').format(values.disk)).data('value', values.disk);
623
                this.disks.append(disk);
624
                this.__added_flavors.disk.push(values.disk)
625
            }
626
            
627
            if (this.__added_flavors.disk_template.indexOf(values.disk_template) == -1) {
628
                var template_info = flv.get_disk_template_info();
629
                var disk_template = $(('<li title="{2}" class="option disk_template value-{0}">' + 
630
                                       '<span class="value name">{1}</span>' +
631
                                       '</li>').format(values.disk_template, 
632
                                            template_info.name, 
633
                                            template_info.description)).data('value', 
634
                                                                values.disk_template);
635

    
636
                this.disk_templates.append(disk_template);
637
                //disk_template.tooltip({position:'top center', offset:[-5,0], delay:100, tipClass:'tooltip disktip'});
638
                this.__added_flavors.disk_template.push(values.disk_template)
639
            }
640
            
641
        },
642
        
643
        get_active_flavors: function() {
644
            return storage.flavors.active();
645
        },
646

    
647
        get_valid_flavors: function() {
648
            return this.flavors;
649
        },
650

    
651
        update_layout: function() {
652
            this.update_selected_flavor();
653
            this.update_disabled_flavors();
654
            this.validate();
655
            this.validate_selected_flavor();
656
        },
657

    
658
        reset: function() {
659
            this.current_image = storage.images.at(0);
660
            this.flavors = [];
661
            this.flavors_data = {'cpu':[], 'mem':[], 'disk':[]};
662
            this.update_flavors_data();
663
        },
664

    
665
        validate: function() {
666
            if (!this.current_flavor) {
667
                this.parent.$(".form-action.next").hide();
668
            } else {
669
                this.parent.$(".form-action.next").show();
670
            }
671
        },
672

    
673
        get: function() {
674
            return {'flavor': this.current_flavor}
675
        }
676

    
677
    });
678

    
679
    views.CreatePersonalizeView = views.CreateVMStepView.extend({
680
        step: 3,
681
        initialize: function() {
682
            views.CreateSubmitView.__super__.initialize.apply(this, arguments);
683
            this.roles = this.$("li.predefined-meta.role .values");
684
            this.name = this.$("input.rename-field");
685
            this.name_changed = false;
686
            this.init_suggested_roles();
687
            this.init_handlers();
688
            this.ssh_list = this.$(".ssh ul");
689
            this.selected_keys = [];
690

    
691
            var self = this;
692
            this.$(".create-ssh-key").click(function() {
693
                var confirm_close = true || confirm("This action will close the virtual machine creation wizard." +
694
                                            " Are you sure you want to continue ?")
695
                if (confirm_close) {
696
                    snf.ui.main.public_keys_view.show(self.parent);
697
                } else {
698
                }
699
            });
700
        },
701

    
702
        init_suggested_roles: function() {
703
            var cont = this.roles;
704
            cont.empty();
705
            
706
            // TODO: get suggested from snf.api.conf
707
            _.each(window.SUGGESTED_ROLES, function(r){
708
                var el = $('<span class="val">{0}</span>'.format(r));
709
                el.data("value", r);
710
                cont.append(el);
711
                el.click(function() {
712
                    $(this).parent().find(".val").removeClass("selected");
713
                    $(this).toggleClass("selected");
714
                })
715
            });
716
            
717
            var self = this;
718
            $(".ssh li.ssh-key-option").live("click", function(e) {
719
                var key = $(this).data("model");
720
                self.select_key(key);
721
            });
722
        },
723

    
724
        select_key: function(key) {
725
            var exists = this.selected_keys.indexOf(key.id);
726
            if (exists > -1) {
727
                this.selected_keys.splice(exists, 1);
728
            } else {
729
                this.selected_keys.push(key.id);
730
            }
731
            this.update_ui_keys_selections(this.selected_keys);
732
        },
733

    
734
        update_ui_keys_selections: function(keys) {
735
            var self = this;
736
            self.$(".ssh-key-option").removeClass("selected");
737
            self.$(".ssh-key-option .check").attr("checked", false);
738
            _.each(keys, function(kid) {
739
                $("#ssh-key-option-" + kid).addClass("selected");
740
                $("#ssh-key-option-" + kid).find(".check").attr("checked", true);
741
            });
742
        },
743

    
744
        update_ssh_keys: function() {
745
            this.ssh_list.empty();
746
            var keys = snf.storage.keys.models;
747
            if (keys.length == 0) { 
748
                this.$(".ssh .empty").show();
749
            } else {
750
                this.$(".ssh .empty").hide();
751
            }
752
            _.each(keys, _.bind(function(key){
753
                var el = $('<li id="ssh-key-option-{1}" class="ssh-key-option">{0}</li>'.format(key.get("name"), key.id));
754
                var check = $('<input class="check" type="checkbox"></input>')
755
                el.append(check);
756
                el.data("model", key);
757
                this.ssh_list.append(el);
758
            }, this));
759
        },
760

    
761
        init_handlers: function() {
762
            this.name.bind("keypress", _.bind(function(e) {
763
                this.name_changed = true;
764
                if (e.keyCode == 13) { this.parent.set_step(4); this.parent.update_layout() };    
765
            }, this));
766

    
767
            this.name.bind("click", _.bind(function() {
768
                if (!this.name_changed) {
769
                    this.name.val("");
770
                }
771
            }, this))
772
        },
773

    
774
        show: function() {
775
            views.CreatePersonalizeView.__super__.show.apply(this, arguments);
776
            this.update_layout();
777
        },
778
        
779
        update_layout: function() {
780
            var params = this.parent.get_params();
781

    
782
            if (!params.image || !params.flavor) { return }
783

    
784
            if (!params.image) { return }
785
            var vm_name_tpl = snf.config.vm_name_template || "My {0} server";
786
            var vm_name = vm_name_tpl.format(params.image.get("name"));
787
            var orig_name = vm_name;
788
            
789
            var existing = true;
790
            var j = 0;
791

    
792
            while (existing && !this.name_changed) {
793
                var existing = storage.vms.select(function(vm){return vm.get("name") == vm_name}).length
794
                if (existing) {
795
                    j++;
796
                    vm_name = orig_name + " " + j;
797
                }
798
            }
799

    
800
            if (!_(this.name.val()).trim() || !this.name_changed) {
801
                this.name.val(vm_name);
802
            }
803

    
804
            if (!this.name_changed && this.parent.visible()) {
805
                if (!$.browser.msie && !$.browser.opera) {
806
                    this.$("#create-vm-name").select();
807
                } else {
808
                    window.setTimeout(_.bind(function(){
809
                        this.$("#create-vm-name").select();
810
                    }, this), 400)
811
                }
812
            }
813
            
814
            var img = snf.ui.helpers.os_icon_path(params.image.get("OS"))
815
            this.name.css({backgroundImage:"url({0})".format(img)})
816
            
817
            if (!params.image.supports('ssh')) {
818
                this.disable_ssh_keys();
819
            } else {
820
                this.enable_ssh_keys();
821
                this.update_ssh_keys();
822
            }
823

    
824
            this.update_ui_keys_selections(this.selected_keys);
825
        },
826

    
827
        disable_ssh_keys: function() {
828
            this.$(".disabled.desc").show();
829
            this.$(".empty.desc").hide();
830
            this.$(".ssh .confirm-params").hide();
831
            this.selected_keys = [];
832
        },
833

    
834
        enable_ssh_keys: function() {
835
            this.$(".ssh .confirm-params").show();
836
            this.$(".disabled.desc").hide();
837
        },
838

    
839
        reset: function() {
840
            this.roles.find(".val").removeClass("selected");
841
            this.name_changed = false;
842
            this.selected_keys = [];
843
            this.update_layout();
844
        },
845

    
846
        get_meta: function() {
847
            if (this.roles.find(".selected").length == 0) {
848
                return false;
849
            }
850

    
851
            var role = $(this.roles.find(".selected").get(0)).data("value");
852
            return {'Role': role }
853
        },
854

    
855
        get: function() {
856
            var val = {'name': this.name.val() };
857
            if (this.get_meta()) {
858
                val.metadata = this.get_meta();
859
            }
860

    
861
            val.keys = _.map(this.selected_keys, function(k){ return snf.storage.keys.get(k)});
862
            
863
            return val;
864
        }
865
    });
866

    
867
    views.CreateSubmitView = views.CreateVMStepView.extend({
868
        step: 4,
869
        initialize: function() {
870
            views.CreateSubmitView.__super__.initialize.apply(this, arguments);
871
            this.roles = this.$("li.predefined-meta.role .values");
872
            this.confirm = this.$(".confirm-params ul");
873
            this.name = this.$("h3.vm-name");
874
            this.keys = this.$(".confirm-params.ssh");
875
            this.meta = this.$(".confirm-params.meta");
876
            this.init_handlers();
877
        },
878

    
879
        init_handlers: function() {
880
        },
881

    
882
        show: function() {
883
            views.CreateSubmitView.__super__.show.apply(this, arguments);
884
            this.update_layout();
885
        },
886
        
887
        update_flavor_details: function() {
888
            var flavor = this.parent.get_params().flavor;
889

    
890
            function set_detail(sel, key) {
891
                var val = key;
892
                if (key == undefined) { val = flavor.get(sel) };
893
                this.$(".confirm-cont.flavor .flavor-" + sel + " .value").text(val)
894
            }
895
            
896
            set_detail("cpu", flavor.get("cpu") + "x");
897
            set_detail("ram", flavor.get("ram") + " MB");
898
            set_detail("disk", util.readablizeBytes(flavor.get("disk") * 1024 * 1024 * 1024));
899
            set_detail("disktype", flavor.get_disk_template_info().name);
900
        },
901

    
902
        update_image_details: function() {
903
            var image = this.parent.get_params().image;
904

    
905
            function set_detail(sel, key) {
906
                var val = key;
907
                if (key == undefined) { val = image.get(sel) };
908
                this.$(".confirm-cont.image .image-" + sel + " .value").text(val)
909
            }
910
            
911
            set_detail("description");
912
            set_detail("name");
913
            set_detail("os", _(image.get("OS")).capitalize());
914
            set_detail("gui", image.get("GUI"));
915
            set_detail("size", image.get_readable_size());
916
            set_detail("kernel");
917
        },
918

    
919
        update_selected_keys: function(keys) {
920
            this.keys.empty();
921
            if (!keys || keys.length == 0) {
922
                this.keys.append(this.make("li", {'class':'empty'}, 'No keys selected'))
923
            }
924
            _.each(keys, _.bind(function(key) {
925
                var el = this.make("li", {'class':'selected-ssh-key'}, key.get('name'));
926
                this.keys.append(el);
927
            }, this))
928
        },
929

    
930
        update_selected_meta: function(meta) {
931
            this.meta.empty();
932
            if (!meta || meta.length == 0) {
933
                this.meta.append(this.make("li", {'class':'empty'}, 'No tags selected'))
934
            }
935
            _.each(meta, _.bind(function(value, key) {
936
                var el = this.make("li", {'class':"confirm-value"});
937
                var name = this.make("span", {'class':"ckey"}, key);
938
                var value = this.make("span", {'class':"cval"}, value);
939

    
940
                $(el).append(name)
941
                $(el).append(value);
942
                this.meta.append(el);
943
            }, this));
944
        },
945

    
946
        update_layout: function() {
947
            var params = this.parent.get_params();
948
            if (!params.image || !params.flavor) { return }
949

    
950
            if (!params.image) { return }
951

    
952
            this.name.text(params.name);
953

    
954
            this.confirm.find("li.image .value").text(params.flavor.get("image"));
955
            this.confirm.find("li.cpu .value").text(params.flavor.get("cpu") + "x");
956
            this.confirm.find("li.mem .value").text(params.flavor.get("ram"));
957
            this.confirm.find("li.disk .value").text(params.flavor.get("disk"));
958

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

    
962
            this.update_image_details();
963
            this.update_flavor_details();
964

    
965
            if (!params.image.supports('ssh')) {
966
                this.keys.hide();
967
                this.keys.prev().hide();
968
            } else {
969
                this.keys.show();
970
                this.keys.prev().show();
971
                this.update_selected_keys(params.keys);
972
            }
973
            
974
            this.update_selected_meta(params.metadata);
975
        },
976

    
977
        reset: function() {
978
            this.update_layout();
979
        },
980

    
981
        get_meta: function() {
982
        },
983

    
984
        get: function() {
985
            return {};
986
        }
987
    });
988

    
989
    views.CreateVMView = views.Overlay.extend({
990
        
991
        view_id: "create_vm_view",
992
        content_selector: "#createvm-overlay-content",
993
        css_class: 'overlay-createvm overlay-info',
994
        overlay_id: "metadata-overlay",
995

    
996
        subtitle: false,
997
        title: "Create new machine",
998

    
999
        initialize: function(options) {
1000
            views.CreateVMView.__super__.initialize.apply(this);
1001
            this.current_step = 1;
1002

    
1003
            this.password_view = new views.VMCreationPasswordView();
1004

    
1005
            this.steps = [];
1006
            this.steps[1] = new views.CreateImageSelectView(this);
1007
            this.steps[1].bind("change", _.bind(function(data) {this.trigger("image:change", data)}, this));
1008

    
1009
            this.steps[2] = new views.CreateFlavorSelectView(this);
1010
            this.steps[3] = new views.CreatePersonalizeView(this);
1011
            this.steps[4] = new views.CreateSubmitView(this);
1012

    
1013
            this.cancel_btn = this.$(".create-controls .cancel");
1014
            this.next_btn = this.$(".create-controls .next");
1015
            this.prev_btn = this.$(".create-controls .prev");
1016
            this.submit_btn = this.$(".create-controls .submit");
1017

    
1018
            this.history = this.$(".steps-history");
1019
            this.history_steps = this.$(".steps-history .steps-history-step");
1020
            
1021
            this.init_handlers();
1022
        },
1023

    
1024
        init_handlers: function() {
1025
            var self = this;
1026
            this.next_btn.click(_.bind(function(){
1027
                this.set_step(this.current_step + 1);
1028
                this.update_layout();
1029
            }, this))
1030
            this.prev_btn.click(_.bind(function(){
1031
                this.set_step(this.current_step - 1);
1032
                this.update_layout();
1033
            }, this))
1034
            this.cancel_btn.click(_.bind(function(){
1035
                this.close_all();
1036
            }, this))
1037
            this.submit_btn.click(_.bind(function(){
1038
                this.submit();
1039
            }, this))
1040
            
1041
            this.history.find(".completed").live("click", function() {
1042
                var step = parseInt($(this).attr("id").replace("vm-create-step-history-", ""));
1043
                self.set_step(step);
1044
                self.update_layout();
1045
            })
1046
        },
1047

    
1048
        set_step: function(st) {
1049
        },
1050
        
1051
        validate: function(data) {
1052
            if (_(data.name).trim() == "") {
1053
                this.$(".form-field").addClass("error");
1054
                return false;
1055
            } else {
1056
                return true;
1057
            }
1058
        },
1059

    
1060
        submit: function() {
1061
            if (this.submiting) { return };
1062
            var data = this.get_params();
1063
            var meta = {};
1064
            var extra = {};
1065
            var personality = [];
1066

    
1067
            if (this.validate(data)) {
1068
                this.submit_btn.addClass("in-progress");
1069
                this.submiting = true;
1070
                if (data.metadata) { meta = data.metadata; }
1071
                if (data.keys && data.keys.length > 0) {
1072
                    personality.push(data.image.personality_data_for_keys(data.keys))
1073
                }
1074

    
1075
                extra['personality'] = personality;
1076
                storage.vms.create(data.name, data.image, data.flavor, meta, extra, _.bind(function(data){
1077
                    this.close_all();
1078
                    this.password_view.show(data.server.adminPass, data.server.id);
1079
                    this.submiting = false;
1080
                }, this));
1081
            }
1082
        },
1083

    
1084
        close_all: function() {
1085
            this.hide();
1086
        },
1087

    
1088
        reset: function() {
1089
            this.current_step = 1;
1090

    
1091
            this.steps[1].reset();
1092
            this.steps[2].reset();
1093
            this.steps[3].reset();
1094
            this.steps[4].reset();
1095

    
1096
            this.steps[1].show();
1097
            this.steps[2].show();
1098
            this.steps[3].show();
1099
            this.steps[4].show();
1100

    
1101
            this.submit_btn.removeClass("in-progress");
1102
        },
1103

    
1104
        onShow: function() {
1105
            this.reset()
1106
            this.update_layout();
1107
        },
1108

    
1109
        update_layout: function() {
1110
            this.show_step(this.current_step);
1111
            this.current_view.update_layout();
1112
        },
1113

    
1114
        beforeOpen: function() {
1115
            if (!this.skip_reset_on_next_open) {
1116
                this.submiting = false;
1117
                this.reset();
1118
                this.current_step = 1;
1119
                this.$(".steps-container").css({"margin-left":0 + "px"});
1120
                this.show_step(1);
1121
            }
1122
            
1123
            this.skip_reset_on_next_open = false;
1124
            this.update_layout();
1125
        },
1126
        
1127
        set_step: function(step) {
1128
            if (step <= 1) {
1129
                step = 1
1130
            }
1131
            if (step > this.steps.length - 1) {
1132
                step = this.steps.length - 1;
1133
            }
1134
            this.current_step = step;
1135
        },
1136

    
1137
        show_step: function(step) {
1138
            this.current_view = this.steps[step];
1139
            this.update_controls();
1140

    
1141
            this.steps[step].show();
1142
            var width = this.el.find('.container').width();
1143
            var left = (step -1) * width * -1;
1144
            this.$(".steps-container").animate({"margin-left": left + "px"}, 300);
1145

    
1146
            this.update_steps_history();
1147
        },
1148

    
1149
        update_steps_history: function() {
1150
            var self = this;
1151
            function get_step(s) {
1152
                return self.history.find(".step" + s + "h");
1153
            }
1154
            
1155
            var current_step = parseInt(this.current_view.step);
1156
            _.each(this.steps, function(stepv) {
1157
                var step = parseInt(stepv.step);
1158
                get_step(step).removeClass("completed").removeClass("current");
1159
                if (step == current_step) {
1160
                    get_step(step).removeClass("completed").addClass("current");
1161
                }
1162
                if (step < current_step) {
1163
                    get_step(step).removeClass("current").addClass("completed");
1164
                }
1165
            });
1166
        },
1167

    
1168
        update_controls: function() {
1169
            var step = this.current_step;
1170
            if (step == 1) {
1171
                this.prev_btn.hide();
1172
                this.cancel_btn.show();
1173
            } else {
1174
                this.prev_btn.show();
1175
                this.cancel_btn.hide();
1176
            }
1177
            
1178
            if (step == this.steps.length - 1) {
1179
                this.next_btn.hide();
1180
                this.submit_btn.show();
1181
            } else {
1182
                this.next_btn.show();
1183
                this.submit_btn.hide();
1184
            }
1185
        },
1186

    
1187
        get_params: function() {
1188
            return _.extend({}, this.steps[1].get(), this.steps[2].get(), this.steps[3].get());
1189
        }
1190
    });
1191
    
1192
})(this);
1193