Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (65.6 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
            }, this));
75

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

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

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

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

    
117

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
290
        },
291

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

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

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

    
312
        },
313

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

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

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

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

    
358
            } else {
359
            }
360

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
667
            this.update_unavailable_values();
668
        },
669

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

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

    
680
            var quotas = synnefo.storage.quotas.get_available_for_vm({active: true});
681
            var user_excluded = storage.flavors.unavailable_values_for_quotas(quotas);
682

    
683
            unavailable.disk = user_excluded.disk.concat(image_excluded.disk);
684
            unavailable.ram = user_excluded.ram.concat(image_excluded.ram);
685
            unavailable.cpu = user_excluded.cpu.concat(image_excluded.cpu);
686
            
687
            this.unavailable_values = unavailable;
688
        },
689
        
690
        flavor_is_valid: function(flv) {
691
            if (!flv) { return false };
692

    
693
            var existing = storage.flavors.get_flavor(flv.get("cpu"), flv.get("ram"), flv.get("disk"), flv.get("disk_template"), this.flavors);
694
            if (!existing) { return false };
695
            
696
            if (this.unavailable_values && (this.unavailable_values.disk.indexOf(parseInt(flv.get("disk")) * 1000) > -1)) {
697
                return false;
698
            }
699
            if (this.unavailable_values && (this.unavailable_values.ram.indexOf(parseInt(flv.get("ram"))) > -1)) {
700
                return false;
701
            }
702
            if (this.unavailable_values && (this.unavailable_values.cpu.indexOf(parseInt(flv.get("cpu"))) > -1)) {
703
                return false;
704
            }
705
            return true;
706
        },
707
            
708
        set_valid_current_for: function(t, val) {
709
            var found = this.flavors[0];
710
            _.each(this.flavors, function(flv) {
711
                if (flv.get(t) == val) {
712
                    found = flv;
713
                }
714
            });
715

    
716
            this.set_current(found);
717
            this.validate_selected_flavor();
718
        },
719

    
720
        set_current: function(flv) {
721

    
722
            if (!flv) {
723
                // user clicked on invalid combination
724
                // force the first available choice for the
725
                // type of option he last clicked
726
                this.set_valid_current_for.apply(this, this.last_choice);
727
                return;
728
            }
729

    
730
            this.current_flavor = flv;
731
            this.trigger("change");
732
            if (this.current_flavor) {
733
                this.update_selected_flavor();
734
                this.update_selected_predefined();
735
            }
736
            
737
            this.validate();
738
        },
739
        
740
        select_default_flavor: function() {
741
               
742
        },
743

    
744
        update_selected_from_ui: function() {
745
            this.set_current(this.ui_selected());
746
        },
747
        
748
        update_disabled_flavors: function() {
749
            this.$(".flavor-options.disk li").removeClass("disabled");
750
            if (!this.unavailable_values) { return }
751
            
752
            this.$("#create-vm-flavor-options .flavor-options.disk li").each(_.bind(function(i, el){
753
                var el_value = $(el).data("value") * 1000;
754
                if (this.unavailable_values.disk.indexOf(el_value) > -1) {
755
                    $(el).addClass("disabled");
756
                    $(el).removeClass("selected");
757
                };
758
            }, this));
759

    
760
            this.$("#create-vm-flavor-options .flavor-options.mem li").each(_.bind(function(i, el){
761
                var el_value = $(el).data("value");
762
                if (this.unavailable_values.ram.indexOf(el_value) > -1) {
763
                    $(el).addClass("disabled");
764
                    $(el).removeClass("selected");
765
                };
766
            }, this));
767

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

    
777
        create_flavors: function() {
778
            var flavors = this.get_active_flavors();
779
            var valid_flavors = this.get_valid_flavors();
780
            this.__added_flavors = {'cpu':[], 'ram':[], 'disk':[], 'disk_template':[] };
781

    
782
            _.each(flavors, _.bind(function(flv){
783
                this.add_flavor(flv);
784
            }, this));
785
            
786
            this.sort_flavors(this.disks);
787
            this.sort_flavors(this.cpus);
788
            this.sort_flavors(this.mems);
789
            this.sort_flavors(this.disk_templates);
790

    
791
            var self = this;
792
            this.$(".flavor-options li.option").click(function(){
793
                var el = $(this);
794

    
795
                if (el.hasClass("disabled")) { return }
796

    
797
                el.parent().find(".option").removeClass("selected");
798
                el.addClass("selected");
799

    
800
                if (el.hasClass("mem")) { self.last_choice = ["ram", $(this).data("value")] }
801
                if (el.hasClass("cpu")) { self.last_choice = ["cpu", $(this).data("value")] }
802
                if (el.hasClass("disk")) { self.last_choice = ["disk", $(this).data("value")] }
803
                if (el.hasClass("disk_template")) { self.last_choice = ["disk_template", $(this).data("value")] }
804

    
805
                self.update_selected_from_ui();
806
            });
807

    
808
            $(".flavor-opts-list").each(function(){
809
              var el = $(this);
810
              if (el.find(".option").length > 6) {
811
                el.addClass("compact");
812
              }
813
            });
814
        },
815

    
816
        sort_flavors: function(els) {
817
            var prev = undefined;
818
            els.find("li").each(function(i,el){
819
                el = $(el);
820
                if (!prev) { prev = el; return true };
821
                if (el.data("value") < prev.data("value")) {
822
                    prev.before(el);
823
                }
824
                prev = el;
825
            })
826
        },
827
        
828
        ui_selected: function() {
829
            var args = [this.$(".option.cpu.selected").data("value"), 
830
                this.$(".option.mem.selected").data("value"), 
831
                this.$(".option.disk.selected").data("value"),
832
                this.$(".option.disk_template.selected").data("value"),
833
            this.flavors];
834
            
835
            var flv = storage.flavors.get_flavor.apply(storage.flavors, args);
836
            return flv;
837
        },
838

    
839
        update_selected_flavor: function() {
840
            var flv = this.current_flavor;
841
            if (!flv) { return }
842
            this.$(".option").removeClass("selected");
843

    
844
            this.$(".option.cpu.value-" + flv.get("cpu")).addClass("selected");
845
            this.$(".option.mem.value-" + flv.get("ram")).addClass("selected");
846
            this.$(".option.disk.value-" + flv.get("disk")).addClass("selected");
847
            this.$(".option.disk_template.value-" + flv.get("disk_template")).addClass("selected");
848
            
849
            var disk_el = this.$(".option.disk_template.value-" + flv.get("disk_template"));
850
            var basebgpos = 470;
851
                
852
            var append_to_bg_pos = 40 + (disk_el.index() * 91);
853
            var bg_pos = basebgpos - append_to_bg_pos;
854

    
855
            this.$(".disk-template-description").css({backgroundPosition:'-' + bg_pos + 'px top'})
856
            this.$(".disk-template-description p").html(flv.get_disk_template_info().description || "");
857
        },
858
        
859
        __added_flavors: {'cpu':[], 'ram':[], 'disk':[], 'disk_template':[]},
860
        add_flavor: function(flv) {
861
            var values = {'cpu': flv.get('cpu'), 
862
                          'mem': flv.get('ram'), 
863
                          'disk': flv.get('disk'), 
864
                          'disk_template': flv.get('disk_template')};
865

    
866
            disabled = "";
867
            
868
            if (this.__added_flavors.cpu.indexOf(values.cpu) == -1) {
869
                var cpu = $(('<li class="option cpu value-{0} {1}">' + 
870
                             '<span class="value">{0}</span>' + 
871
                             '<span class="metric">x</span></li>').format(
872
                            _.escape(values.cpu), disabled)).data('value', values.cpu);
873
                this.cpus.append(cpu);
874
                this.__added_flavors.cpu.push(values.cpu);
875
            }
876

    
877
            if (this.__added_flavors.ram.indexOf(values.mem) == -1) {
878
                var mem_value = parseInt(_.escape(values.mem))*1024*1024;
879
                var displayvalue = synnefo.util.readablizeBytes(mem_value, 
880
                                                               0).split(" ");
881
                var mem = $(('<li class="option mem value-{2}">' + 
882
                             '<span class="value">{0}</span>' + 
883
                             '<span class="metric">{1}</span></li>').format(
884
                          displayvalue[0], displayvalue[1], values.mem)).data(
885
                          'value', values.mem);
886
                this.mems.append(mem);
887
                this.__added_flavors.ram.push(values.mem);
888
            }
889

    
890
            if (this.__added_flavors.disk.indexOf(values.disk) == -1) {
891
                var disk = $(('<li class="option disk value-{0}">' + 
892
                              '<span class="value">{0}</span>' + 
893
                              '<span class="metric">GB</span></li>').format(
894
                            _.escape(values.disk))).data('value', values.disk);
895
                this.disks.append(disk);
896
                this.__added_flavors.disk.push(values.disk)
897
            }
898
            
899
            if (this.__added_flavors.disk_template.indexOf(values.disk_template) == -1) {
900
                var template_info = flv.get_disk_template_info();
901
                var disk_template = $(('<li title="{2}" class="option disk_template value-{0}">' + 
902
                                       '<span class="value name">{1}</span>' +
903
                                       '</li>').format(values.disk_template, 
904
                                            _.escape(template_info.name), 
905
                                            template_info.description)).data('value', 
906
                                                                values.disk_template);
907

    
908
                this.disk_templates.append(disk_template);
909
                //disk_template.tooltip({position:'top center', offset:[-5,0], delay:100, tipClass:'tooltip disktip'});
910
                this.__added_flavors.disk_template.push(values.disk_template)
911
            }
912
            
913
        },
914
        
915
        get_active_flavors: function() {
916
            return storage.flavors.active();
917
        },
918

    
919
        get_valid_flavors: function() {
920
            return this.flavors;
921
        },
922

    
923
        update_layout: function() {
924
            this.update_selected_flavor();
925
            this.update_disabled_flavors();
926
            this.validate();
927
            this.validate_selected_flavor();
928
            this.update_quota_display();
929
        },
930
        
931
        update_quota_display: function() {
932

    
933
          var quotas = synnefo.storage.quotas;
934
          _.each(["disk", "ram", "cpu"], function(type) {
935
            var active = true;
936
            var key = 'available';
937
            var available_dsp = quotas.get('cyclades.'+type).get_readable(key, active);
938
            var available = quotas.get('cyclades.'+type).get_available(key);
939
            var content = "({0} left)".format(available_dsp);
940
            if (available <= 0) { content = "(None left)" }
941
            
942
            if (type == "ram") { type = "mem" }
943
            $(".flavor-options."+type+" h4 .available").text(content);
944
            if (available <= 0) {
945
              $(".flavor-options."+type+" h4 .available").addClass("error");
946
            } else {
947
              $(".flavor-options."+type+" h4 .available").removeClass("error");
948
            }
949
          })
950
        },
951

    
952
        reset: function() {
953
            this.current_image = storage.images.at(0);
954
            this.flavors = [];
955
            this.flavors_data = {'cpu':[], 'mem':[], 'disk':[]};
956
            this.update_flavors_data();
957
        },
958

    
959
        validate: function() {
960
            if (!this.current_flavor) {
961
                this.parent.$(".form-action.next").hide();
962
            } else {
963
                this.parent.$(".form-action.next").show();
964
            }
965
        },
966

    
967
        get: function() {
968
            return {'flavor': this.current_flavor}
969
        }
970
    });
971
    
972

    
973
    views.CreateColumnSelectOptionView = bb.View.extend({
974
        tagName: 'li',
975
        el: undefined,
976
        model: undefined,
977
        id_prefix: 'model-',
978
        tpl: '<input type="checkbox" class="check"/><span class="title"></span>',
979
        className: 'list-item-option clearfix',
980
        events: {
981
          'click': 'handle_click'
982
        },
983

    
984
        initialize: function(options) {
985
          _.bindAll(this);
986
          this.model.bind("change", this.render);
987
          this.model.bind("remove", this.remove);
988
          this.selected = false;
989
          if (options.get_model_title) {
990
            this.get_model_title = _.bind(options.get_model_title, this);
991
          }
992
          this.model_title_attr = options.model_title_attr;
993
          $(this.el).append($(this.tpl));
994
        },
995
        
996
        id: function() {
997
          return this.id_prefix + this.model && this.model.id || '';
998
        },
999
        
1000
        handle_click: function() {
1001
          this.selected = !this.selected;
1002
          this.render();
1003
        },
1004

    
1005
        remove: function() {
1006
          this.model.unbind("change", this.render);
1007
          this.model.unbind("remove", this.remove);
1008
        },
1009
        
1010
        get_model_title: function() {
1011
          return this.model.get(this.model_title_attr || 'id');
1012
        },
1013

    
1014
        render: function() {
1015
          $(this.el).find(".title").text(this.get_model_title());
1016
          $(this.el).toggleClass('selected', this.selected);
1017
          if (this.selected) {
1018
            $(this.el).find("input").attr("checked", true);
1019
          } else {
1020
            $(this.el).find("input").attr("checked", false);
1021
          }
1022
        }
1023
    });
1024
    
1025
    views.CreateColumnIPOptionView = views.CreateColumnSelectOptionView.extend({
1026
      get_model_title: function() {
1027
        return this.model.get('ip');
1028
      }
1029
    });
1030

    
1031
    views.CreateColumnPrivateNetworkOptionView = views.CreateColumnSelectOptionView.extend({
1032
      get_model_title: function() {
1033
        return this.model.get('name');
1034
      }
1035
    });
1036

    
1037
    views.CreateColumnSelectListView = bb.View.extend({
1038
        collection: undefined,
1039
        header: undefined,
1040
        tagName: 'div',
1041
        extra_class: '',
1042
        el: undefined,
1043
        title_tpl: undefined,
1044
        title: 'List view',
1045
        description: 'List view description.',
1046
        empty_msg: 'No entries.',
1047
        item_cls: views.CreateColumnSelectOptionView,
1048
        className: 'list-cont create-column-select personalize-cont',
1049

    
1050
        initialize: function(options) {
1051
          _.bindAll(this);
1052
          if (options.extra_class) {
1053
            $(this.el).addClass(options.extra_class);
1054
          }
1055
          this.update_collection = options.update_collection;
1056
          this.title = options.title || this.title;
1057
          this.titple_tpl = options.title_tpl || this.title_tpl;
1058
          this.description = options.description || this.description;
1059
          this.empty_msg = options.empty_msg || this.empty_msg;
1060
          this.item_cls = options.item_cls || this.item_cls;
1061
          this.select_first_as_default = options.select_first_as_default;
1062
          this.filter_items = options.filter_items;
1063
          this.post_render_entries = options.post_render_entries || function() {};
1064
          this.init_events = options.init_events || function() {};
1065

    
1066
          this.init_events = _.bind(this.init_events, this);
1067
          this.post_render_entries = _.bind(this.post_render_entries, this);
1068

    
1069
          this._ul = $('<ul class="confirm-params">');
1070
          this._title = $("<h4>");
1071
          this._description = $("<p class='desc'>");
1072
          this._empty = $("<p class='empty hidden desc'>");
1073
          this._empty.html(this.empty_msg);
1074
        
1075
          this.item_views = [];
1076

    
1077
          $(this.el).append(this._title);
1078
          $(this.el).append(this._description);
1079
          $(this.el).append(this._empty);
1080
          $(this.el).append(this._ul);
1081

    
1082
          this['$el'] = $(this.el);
1083

    
1084
          if (!this.title_tpl) { this.title_tpl = this.title };
1085

    
1086
          this.collection.bind("change", this.render_entries);
1087
          this.collection.bind("reset", this.render_entries);
1088
          this.collection.bind("add", this.render_entries);
1089
          this.collection.bind("remove", this.remove_entry);
1090
          
1091
          this.fetcher = undefined;
1092
          if (this.update_collection) {
1093
              this.fetcher_params = [snf.config.update_interval, 
1094
                    snf.config.update_interval_increase || 500,
1095
                    snf.config.fast_interval || snf.config.update_interval/2, 
1096
                    snf.config.update_interval_increase_after_calls || 4,
1097
                    snf.config.update_interval_max || 20000,
1098
                    true, 
1099
                    {is_recurrent: true, update: true}];
1100
              this.fetcher = this.collection.get_fetcher.apply(this.collection, 
1101
                                                _.clone(this.fetcher_params));
1102
              this.fetcher.start();
1103
          }
1104
          this.render();
1105
          this.init_events();
1106
        },
1107
        
1108
        render: function() {
1109
          this._title.html(this.title_tpl);
1110
          this._description.html(this.description);
1111
          this.render_entries();
1112
        },
1113
        
1114
        remove_entry: function(model) {
1115
          if (!this.item_views[model.id]) { return }
1116
          this.item_views[model.id].remove();
1117
          delete this.item_views[model.pk]
1118
        },
1119
        
1120
        get_selected: function() {
1121
          return _.map(_.filter(this.item_views, function(v) { 
1122
            return v.selected
1123
          }), function(v) {
1124
            return v.model
1125
          });
1126
        },
1127
        
1128
        check_empty: function() {
1129
          if (this.item_views.length == 0) {
1130
            this._empty.show();
1131
          } else {
1132
            this._empty.hide();
1133
          }
1134
        },
1135

    
1136
        render_entries: function() {
1137
          var entries;
1138
          if (this.filter_items) {
1139
            entries = this.collection.filter(this.filter_items);
1140
          } else {
1141
            entries = this.collection.models;
1142
          }
1143
          
1144
          var selected = this.get_selected();
1145
          
1146
          _.each(entries, _.bind(function(model) {
1147
            if (this.item_views[model.id]) {
1148
              this.item_views[model.id].render();
1149
            } else {
1150
              var view = new this.item_cls({model:model});
1151
              if (!selected.length && this.select_first_as_default) { 
1152
                view.selected = true; selected = [1] 
1153
              }
1154
              view.render();
1155
              this.item_views[model.id] = view;
1156
              this._ul.append($(view.el));
1157
            }
1158
          }, this));
1159
          this.check_empty();
1160
          this.post_render_entries();
1161
        },
1162

    
1163
        remove: function() {
1164
          _.each(this.item_views, function(v){
1165
            v.remove();
1166
          });
1167
          if (this.update_collection) { this.fetcher.stop() }
1168
          this.unbind();
1169
          this.collection.unbind("change", this.render_entries);
1170
          this.collection.unbind("reset", this.render_entries);
1171
          this.collection.unbind("add", this.render_entries);
1172
          this.collection.unbind("remove", this.remove_entry);
1173
          views.CreateColumnSelectListView.__super__.remove.apply(this, arguments);
1174
        }
1175
    });
1176

    
1177
    views.CreateNetworkingView = views.CreateVMStepView.extend({
1178
        step: 3,
1179
        initialize: function() {
1180
            views.CreateNetworkingView.__super__.initialize.apply(this, arguments);
1181
            this.init_handlers();
1182
            this.selected_keys = [];
1183
            this.cont = this.$(".step-cont");
1184
        },
1185
        
1186
        init_subviews: function() {
1187
            var create_view = this.parent;
1188
            if (!this.networks_view) {
1189
              this.networks_view = new views.NetworkSelectView({
1190
                container: this.cont
1191
              });
1192
              this.networks_view.hide(true);
1193
            }
1194
        },
1195

    
1196
        init_handlers: function() {
1197
        },
1198

    
1199
        show: function() {
1200
            views.CreateNetworkingView.__super__.show.apply(this, arguments);
1201
            this.init_subviews();
1202
            this.update_layout();
1203
            this.networks_view.show(true);
1204
        },
1205
        
1206
        hide_step: function() {
1207
            this.networks_view && this.networks_view.hide(true);
1208
        },
1209

    
1210
        update_layout: function() {
1211
        },
1212

    
1213
        reset: function() {
1214
            this.selected_keys = [];
1215
            this.update_layout();
1216
        },
1217
        
1218
        get_selected_networks: function() {
1219
            if (!this.networks_view) { return [] }
1220
            return this.networks_view.get_selected_networks();
1221
        },
1222
        
1223
        get_selected_addresses: function() {
1224
            if (!this.networks_view) { return [] }
1225
            return this.networks_view.get_selected_floating_ips();
1226
        },
1227

    
1228
        get: function() {
1229
            return {
1230
              'addresses': this.get_selected_addresses(),
1231
              'networks': this.get_selected_networks()
1232
            }
1233
        },
1234

    
1235
        remove: function() {
1236
          if (this.networks_view) {
1237
            this.networks_view.remove();
1238
            delete this.networks_view;
1239
          }
1240
        }
1241
    });
1242

    
1243
    views.CreatePersonalizeView = views.CreateVMStepView.extend({
1244
        step: 4,
1245
        initialize: function() {
1246
            views.CreateSubmitView.__super__.initialize.apply(this, arguments);
1247
            this.roles = this.$("li.predefined-meta.role .values");
1248
            this.name = this.$("input.rename-field");
1249
            this.name_changed = false;
1250
            this.init_suggested_roles();
1251
            this.init_handlers();
1252
            this.ssh_list = this.$(".ssh ul");
1253
            this.selected_keys = [];
1254

    
1255
            var self = this;
1256
            this.$(".create-ssh-key").click(function() {
1257
                var confirm_close = true;
1258
                if (confirm_close) {
1259
                    snf.ui.main.public_keys_view.show(self.parent);
1260
                } else {
1261
                }
1262
            });
1263
        },
1264

    
1265
        init_suggested_roles: function() {
1266
            var cont = this.roles;
1267
            cont.empty();
1268
            
1269
            // TODO: get suggested from snf.api.conf
1270
            _.each(window.SUGGESTED_ROLES, function(r){
1271
                var el = $('<span class="val">{0}</span>'.format(_.escape(r)));
1272
                el.data("value", r);
1273
                cont.append(el);
1274
                el.click(function() {
1275
                    $(this).parent().find(".val").removeClass("selected");
1276
                    $(this).toggleClass("selected");
1277
                })
1278
            });
1279
            
1280
            var self = this;
1281
            $(".ssh li.ssh-key-option").live("click", function(e) {
1282
                var key = $(this).data("model");
1283
                self.select_key(key);
1284
            });
1285
        },
1286

    
1287
        select_key: function(key) {
1288
            var exists = this.selected_keys.indexOf(key.id);
1289
            if (exists > -1) {
1290
                this.selected_keys.splice(exists, 1);
1291
            } else {
1292
                this.selected_keys.push(key.id);
1293
            }
1294
            this.update_ui_keys_selections(this.selected_keys);
1295
        },
1296

    
1297
        update_ui_keys_selections: function(keys) {
1298
            var self = this;
1299
            self.$(".ssh-key-option").removeClass("selected");
1300
            self.$(".ssh-key-option .check").attr("checked", false);
1301
            _.each(keys, function(kid) {
1302
                $("#ssh-key-option-" + kid).addClass("selected");
1303
                $("#ssh-key-option-" + kid).find(".check").attr("checked", true);
1304
            });
1305
        },
1306

    
1307
        update_ssh_keys: function() {
1308
            this.ssh_list.empty();
1309
            var keys = snf.storage.keys.models;
1310
            if (keys.length == 0) { 
1311
                this.$(".ssh .empty").show();
1312
            } else {
1313
                this.$(".ssh .empty").hide();
1314
            }
1315
            _.each(keys, _.bind(function(key){
1316
                var el = $('<li id="ssh-key-option-{1}" class="ssh-key-option">{0}</li>'.format(_.escape(key.get("name")), key.id));
1317
                var check = $('<input class="check" type="checkbox"></input>')
1318
                el.append(check);
1319
                el.data("model", key);
1320
                this.ssh_list.append(el);
1321
            }, this));
1322
        },
1323

    
1324
        init_handlers: function() {
1325
            this.name.bind("keypress", _.bind(function(e) {
1326
                this.name_changed = true;
1327
                if (e.keyCode == 13) { this.parent.set_step(5); this.parent.update_layout() };    
1328
            }, this));
1329

    
1330
            this.name.bind("click", _.bind(function() {
1331
                if (!this.name_changed) {
1332
                    this.name.val("");
1333
                }
1334
            }, this))
1335
        },
1336

    
1337
        show: function() {
1338
            views.CreatePersonalizeView.__super__.show.apply(this, arguments);
1339
            this.update_layout();
1340
        },
1341
        
1342
        update_layout: function() {
1343
            var params = this.parent.get_params();
1344

    
1345
            if (!params.image || !params.flavor) { return }
1346

    
1347
            if (!params.image) { return }
1348
            var vm_name_tpl = snf.config.vm_name_template || "My {0} server";
1349
            var vm_name = vm_name_tpl.format(_.escape(params.image.get("name")));
1350
            var orig_name = vm_name;
1351
            
1352
            var existing = true;
1353
            var j = 0;
1354

    
1355
            while (existing && !this.name_changed) {
1356
                var existing = storage.vms.select(function(vm){
1357
                  return vm.get("name") == vm_name
1358
                }).length;
1359
                if (existing) {
1360
                    j++;
1361
                    vm_name = orig_name + " " + j;
1362
                }
1363
            }
1364

    
1365
            if (!_(this.name.val()).trim() || !this.name_changed) {
1366
                this.name.val(vm_name);
1367
            }
1368

    
1369
            if (!this.name_changed && this.parent.visible()) {
1370
                if (!$.browser.msie && !$.browser.opera) {
1371
                    this.$("#create-vm-name").select();
1372
                } else {
1373
                    window.setTimeout(_.bind(function(){
1374
                        this.$("#create-vm-name").select();
1375
                    }, this), 400)
1376
                }
1377
            }
1378
            
1379
            var img = snf.ui.helpers.os_icon_path(params.image.get("OS"))
1380
            this.name.css({backgroundImage:"url({0})".format(img)})
1381
            
1382
            if (!params.image.supports('ssh')) {
1383
                this.disable_ssh_keys();
1384
            } else {
1385
                this.enable_ssh_keys();
1386
                this.update_ssh_keys();
1387
            }
1388

    
1389
            this.update_ui_keys_selections(this.selected_keys);
1390
        },
1391

    
1392
        disable_ssh_keys: function() {
1393
            this.$(".disabled.desc").show();
1394
            this.$(".empty.desc").hide();
1395
            this.$(".ssh .confirm-params").hide();
1396
            this.selected_keys = [];
1397
        },
1398

    
1399
        enable_ssh_keys: function() {
1400
            this.$(".ssh .confirm-params").show();
1401
            this.$(".disabled.desc").hide();
1402
        },
1403

    
1404
        reset: function() {
1405
            this.roles.find(".val").removeClass("selected");
1406
            this.name_changed = false;
1407
            this.selected_keys = [];
1408
            this.update_layout();
1409
        },
1410

    
1411
        get_meta: function() {
1412
            if (this.roles.find(".selected").length == 0) {
1413
                return false;
1414
            }
1415

    
1416
            var role = $(this.roles.find(".selected").get(0)).data("value");
1417
            return {'Role': role }
1418
        },
1419

    
1420
        get: function() {
1421
            var val = {'name': this.name.val() };
1422
            if (this.get_meta()) {
1423
                val.metadata = this.get_meta();
1424
            }
1425

    
1426
            val.keys = _.map(this.selected_keys, function(k){ return snf.storage.keys.get(k)});
1427
            
1428
            return val;
1429
        }
1430
    });
1431

    
1432
    views.CreateSubmitView = views.CreateVMStepView.extend({
1433
        step: 5,
1434
        initialize: function() {
1435
            views.CreateSubmitView.__super__.initialize.apply(this, arguments);
1436
            this.roles = this.$("li.predefined-meta.role .values");
1437
            this.confirm = this.$(".confirm-params ul");
1438
            this.name = this.$("h3.vm-name");
1439
            this.keys = this.$(".confirm-params.ssh");
1440
            this.meta = this.$(".confirm-params.meta");
1441
            this.ip_addresses = this.$(".confirm-params.ip-addresses");
1442
            this.private_networks = this.$(".confirm-params.private-networks");
1443
            this.init_handlers();
1444
        },
1445

    
1446
        init_handlers: function() {
1447
        },
1448

    
1449
        show: function() {
1450
            views.CreateSubmitView.__super__.show.apply(this, arguments);
1451
            this.update_layout();
1452
        },
1453
        
1454
        update_network_details: function() {
1455
            var data = this.parent.get_params();
1456
            var ips = data.addresses;
1457
            var networks = data.networks;
1458

    
1459
            this.ip_addresses.empty();
1460
            if (!ips|| ips.length == 0) {
1461
                this.ip_addresses.append(this.make("li", {'class':'empty'}, 
1462
                                           'No ip addresses selected'))
1463
            }
1464
            _.each(ips, _.bind(function(ip) {
1465
                var el = this.make("li", {'class':'selected-ip-address'}, 
1466
                                  ip.get('floating_ip_address'));
1467
                this.ip_addresses.append(el);
1468
            }, this))
1469

    
1470
            this.private_networks.empty();
1471
            if (!networks || networks.length == 0) {
1472
                this.private_networks.append(this.make("li", {'class':'empty'}, 
1473
                                             'No private networks selected'))
1474
            }
1475
            _.each(networks, _.bind(function(network) {
1476
                var el = this.make("li", {'class':'selected-private-network'}, 
1477
                                  network.get('name'));
1478
                this.private_networks.append(el);
1479
            }, this))
1480

    
1481
        },
1482

    
1483
        update_flavor_details: function() {
1484
            var flavor = this.parent.get_params().flavor;
1485

    
1486
            function set_detail(sel, key) {
1487
                var val = key;
1488
                if (key == undefined) { val = flavor.get(sel) };
1489
                this.$(".confirm-cont.flavor .flavor-" + sel + " .value").text(val)
1490
            }
1491
            
1492
            set_detail("cpu", flavor.get("cpu") + "x");
1493
            set_detail("ram", flavor.get("ram") + " MB");
1494
            set_detail("disk", util.readablizeBytes(flavor.get("disk") * 1024 * 1024 * 1024));
1495
            set_detail("disktype", flavor.get_disk_template_info().name);
1496
        },
1497

    
1498
        update_image_details: function() {
1499
            var image = this.parent.get_params().image;
1500

    
1501
            function set_detail(sel, key) {
1502
                var val = key;
1503
                if (key == undefined) { val = image.get(sel) };
1504
                this.$(".confirm-cont.image .image-" + sel + " .value").text(val)
1505
            }
1506
            
1507
            set_detail("description", image.get_description());
1508
            set_detail("name");
1509
            set_detail("os", _(image.get_os()).capitalize());
1510
            set_detail("gui", image.get_gui());
1511
            set_detail("size", _.escape(image.get_readable_size()));
1512
            set_detail("kernel");
1513
        },
1514

    
1515
        update_selected_keys: function(keys) {
1516
            this.keys.empty();
1517
            if (!keys || keys.length == 0) {
1518
                this.keys.append(this.make("li", {'class':'empty'}, 'No keys selected'))
1519
            }
1520
            _.each(keys, _.bind(function(key) {
1521
                var el = this.make("li", {'class':'selected-ssh-key'}, key.get('name'));
1522
                this.keys.append(el);
1523
            }, this))
1524
        },
1525

    
1526
        update_selected_meta: function(meta) {
1527
            this.meta.empty();
1528
            if (!meta || meta.length == 0) {
1529
                this.meta.append(this.make("li", {'class':'empty'}, 'No tags selected'))
1530
            }
1531
            _.each(meta, _.bind(function(value, key) {
1532
                var el = this.make("li", {'class':'confirm-value'});
1533
                var name = this.make("span", {'class':'ckey'}, key);
1534
                var value = this.make("span", {'class':'cval'}, value);
1535

    
1536
                $(el).append(name)
1537
                $(el).append(value);
1538
                this.meta.append(el);
1539
            }, this));
1540
        },
1541

    
1542
        update_layout: function() {
1543
            var params = this.parent.get_params();
1544
            if (!params.image || !params.flavor) { return }
1545

    
1546
            if (!params.image) { return }
1547

    
1548
            this.name.text(params.name);
1549

    
1550
            this.confirm.find("li.image .value").text(params.flavor.get("image"));
1551
            this.confirm.find("li.cpu .value").text(params.flavor.get("cpu") + "x");
1552
            this.confirm.find("li.mem .value").text(params.flavor.get("ram"));
1553
            this.confirm.find("li.disk .value").text(params.flavor.get("disk"));
1554

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

    
1558
            this.update_image_details();
1559
            this.update_flavor_details();
1560
            this.update_network_details();
1561

    
1562
            if (!params.image.supports('ssh')) {
1563
                this.keys.hide();
1564
                this.keys.prev().hide();
1565
            } else {
1566
                this.keys.show();
1567
                this.keys.prev().show();
1568
                this.update_selected_keys(params.keys);
1569
            }
1570
            
1571
            this.update_selected_meta(params.metadata);
1572
        },
1573

    
1574
        reset: function() {
1575
            this.update_layout();
1576
        },
1577

    
1578
        get_meta: function() {
1579
        },
1580

    
1581
        get: function() {
1582
            return {};
1583
        }
1584
    });
1585

    
1586
    views.CreateVMView = views.Overlay.extend({
1587
        
1588
        view_id: "create_vm_view",
1589
        content_selector: "#createvm-overlay-content",
1590
        css_class: 'overlay-createvm overlay-info',
1591
        overlay_id: "metadata-overlay",
1592

    
1593
        subtitle: false,
1594
        title: "Create new machine",
1595

    
1596
        initialize: function(options) {
1597
            views.CreateVMView.__super__.initialize.apply(this);
1598
            this.current_step = 1;
1599

    
1600
            this.password_view = new views.VMCreationPasswordView();
1601

    
1602
            this.steps = [];
1603
            this.steps[1] = new views.CreateImageSelectView(this);
1604
            this.steps[1].bind("change", _.bind(function(data) {this.trigger("image:change", data)}, this));
1605

    
1606
            this.steps[2] = new views.CreateFlavorSelectView(this);
1607
            this.steps[3] = new views.CreateNetworkingView(this);
1608
            this.steps[4] = new views.CreatePersonalizeView(this);
1609
            this.steps[5] = new views.CreateSubmitView(this);
1610

    
1611
            this.cancel_btn = this.$(".create-controls .cancel");
1612
            this.next_btn = this.$(".create-controls .next");
1613
            this.prev_btn = this.$(".create-controls .prev");
1614
            this.submit_btn = this.$(".create-controls .submit");
1615

    
1616
            this.history = this.$(".steps-history");
1617
            this.history_steps = this.$(".steps-history .steps-history-step");
1618
            
1619
            this.init_handlers();
1620
        },
1621

    
1622
        init_handlers: function() {
1623
            var self = this;
1624
            this.next_btn.click(_.bind(function(){
1625
                this.set_step(this.current_step + 1);
1626
                this.update_layout();
1627
            }, this))
1628
            this.prev_btn.click(_.bind(function(){
1629
                this.set_step(this.current_step - 1);
1630
                this.update_layout();
1631
            }, this))
1632
            this.cancel_btn.click(_.bind(function(){
1633
                this.close_all();
1634
            }, this))
1635
            this.submit_btn.click(_.bind(function(){
1636
                this.submit();
1637
            }, this))
1638
            
1639
            this.history.find(".completed").live("click", function() {
1640
                var step = parseInt($(this).attr("id").replace("vm-create-step-history-", ""));
1641
                self.set_step(step);
1642
                self.update_layout();
1643
            })
1644
        },
1645

    
1646
        set_step: function(st) {
1647
        },
1648
        
1649
        validate: function(data) {
1650
            if (_(data.name).trim() == "") {
1651
                this.$(".form-field").addClass("error");
1652
                return false;
1653
            } else {
1654
                return true;
1655
            }
1656
        },
1657

    
1658
        submit: function() {
1659
            if (this.submiting) { return };
1660
            var data = this.get_params();
1661
            var meta = {};
1662
            var extra = {};
1663
            var personality = [];
1664

    
1665
            if (this.validate(data)) {
1666
                this.submit_btn.addClass("in-progress");
1667
                this.submiting = true;
1668
                if (data.metadata) { meta = data.metadata; }
1669
                if (data.keys && data.keys.length > 0) {
1670
                    personality.push(
1671
                      data.image.personality_data_for_keys(data.keys))
1672
                }
1673

    
1674
                if (personality.length) {
1675
                    extra['personality'] = _.flatten(personality);
1676
                }
1677
                
1678
                extra['networks'] = [];
1679
                _.each(data.networks, function(n) {
1680
                  extra.networks.push({'uuid': n.get('id')})
1681
                });
1682
                _.each(data.ips, function(ip) {
1683
                  extra.networks.push({
1684
                    'uuid': ip.get('network').get('id'),
1685
                    'fixed_ip': ip.get('floating_ip_address')
1686
                  });
1687
                });
1688

    
1689
                _.map(data.networks, function(n) { return n.get('id') });
1690
                storage.vms.create(data.name, data.image, data.flavor, 
1691
                                   meta, extra, _.bind(function(data){
1692
                    _.each(data.addresses, function(ip) {
1693
                      ip.set({'status': 'connecting'});
1694
                    });
1695
                    this.close_all();
1696
                    this.password_view.show(data.server.adminPass, 
1697
                                            data.server.id);
1698
                    this.submiting = false;
1699
                }, this));
1700
            }
1701
        },
1702

    
1703
        close_all: function() {
1704
          this.hide();
1705
        },
1706

    
1707
        onClose: function() {
1708
          this.steps[3].remove();
1709
        },
1710

    
1711
        reset: function() {
1712
          this.current_step = 1;
1713

    
1714
          this.steps[1].reset();
1715
          this.steps[2].reset();
1716
          this.steps[3].reset();
1717
          this.steps[4].reset();
1718

    
1719
          //this.steps[1].show();
1720
          //this.steps[2].show();
1721
          //this.steps[3].show();
1722
          //this.steps[4].show();
1723

    
1724
          this.submit_btn.removeClass("in-progress");
1725
        },
1726

    
1727
        onShow: function() {
1728
        },
1729

    
1730
        update_layout: function() {
1731
            this.show_step(this.current_step);
1732
            this.current_view.update_layout();
1733
        },
1734

    
1735
        beforeOpen: function() {
1736
            if (!this.skip_reset_on_next_open) {
1737
                this.submiting = false;
1738
                this.reset();
1739
                this.current_step = 1;
1740
                this.$(".steps-container").css({"margin-left":0 + "px"});
1741
                this.show_step(1);
1742
            }
1743
            
1744
            this.skip_reset_on_next_open = false;
1745
            this.update_layout();
1746
        },
1747
        
1748
        set_step: function(step) {
1749
            if (step <= 1) {
1750
                step = 1
1751
            }
1752
            if (step > this.steps.length - 1) {
1753
                step = this.steps.length - 1;
1754
            }
1755
            this.current_step = step;
1756
        },
1757

    
1758
        show_step: function(step) {
1759
            // FIXME: this shouldn't be here
1760
            // but since we are not calling step.hide this should work
1761
            this.steps[1].image_details.hide();
1762
            
1763
            this.current_view && this.current_view.hide_step && this.current_view.hide_step();
1764
            this.current_view = this.steps[step];
1765
            this.update_controls();
1766

    
1767
            this.steps[step].show();
1768
            var width = this.el.find('.container').width();
1769
            var left = (step -1) * width * -1;
1770
            this.$(".steps-container").animate({"margin-left": left + "px"}, 300);
1771

    
1772
            this.update_steps_history();
1773
        },
1774

    
1775
        update_steps_history: function() {
1776
            var self = this;
1777
            function get_step(s) {
1778
                return self.history.find(".step" + s + "h");
1779
            }
1780
            
1781
            var current_step = parseInt(this.current_view.step);
1782
            _.each(this.steps, function(stepv) {
1783
                var step = parseInt(stepv.step);
1784
                get_step(step).removeClass("completed").removeClass("current");
1785
                if (step == current_step) {
1786
                    get_step(step).removeClass("completed").addClass("current");
1787
                }
1788
                if (step < current_step) {
1789
                    get_step(step).removeClass("current").addClass("completed");
1790
                }
1791
            });
1792
        },
1793

    
1794
        update_controls: function() {
1795
            var step = this.current_step;
1796
            if (step == 1) {
1797
                this.prev_btn.hide();
1798
                this.cancel_btn.show();
1799
            } else {
1800
                this.prev_btn.show();
1801
                this.cancel_btn.hide();
1802
            }
1803
            
1804
            if (step == this.steps.length - 1) {
1805
                this.next_btn.hide();
1806
                this.submit_btn.show();
1807
            } else {
1808
                this.next_btn.show();
1809
                this.submit_btn.hide();
1810
            }
1811
        },
1812

    
1813
        get_params: function() {
1814
            return _.extend({}, this.steps[1].get(), this.steps[2].get(), this.steps[3].get(), this.steps[4].get());
1815
        }
1816
    });
1817
    
1818
})(this);
1819