Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / static / snf / js / ui / web / ui_networks_view.js @ 236223aa

History | View | Annotate | Download (35.2 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 || {};
46
    var views = snf.views = snf.views || {}
47

    
48
    // shortcuts
49
    var bb = root.Backbone;
50
    
51
    // logging
52
    var logger = new snf.logging.logger("SNF-VIEWS");
53
    var debug = _.bind(logger.debug, logger);
54
    
55
    
56
    views.NetworkConnectVMsOverlay = views.Overlay.extend({
57
        title: "Connect machine",
58
        overlay_id: "overlay-select-vms",
59
        content_selector: "#network-vms-select-content",
60
        css_class: "overlay-info",
61

    
62
        initialize: function() {
63
            views.NetworkConnectVMsOverlay.__super__.initialize.apply(this);
64
            this.list = this.$(".vms-list ul");
65
            this.empty_message = this.$(".empty-message");
66

    
67
            // flag for submit handler to avoid duplicate bindings
68
            this.submit_handler_set = false;
69
        },
70
        
71
        init_handlers: function() {
72
            var self = this;
73
            this.list.find("li").click(function(){
74
                $(this).toggleClass("selected");
75
            });
76
            
77
            if (!this.submit_handler_set) {
78
                // avoid duplicate submits
79
                this.el.find(".create").click(_.bind(function() {
80
                    this.submit();
81
                }, this));
82
                this.submit_handler_set = true;
83
            }
84
        },
85

    
86
        reset: function() {
87
            this.list.find("li").remove();
88
        },
89

    
90
        beforeOpen: function() {
91
            this.reset();
92
            this.update_layout();
93
        },
94
        
95
        vm: function(vm) {
96
            if (vm.id) { var id = vm.id } else {var id = vm}
97
            return this.list.find(".vm-" + id);
98
        },
99

    
100
        get_selected: function() {
101
            return this.list.find(".selected").map(function() {return $(this).data('vm')})
102
        },
103

    
104
        update_layout: function() {
105
            if (this.vms.length == 0) {
106
                this.empty_message.show();
107
            } else {
108
                this.empty_message.hide();
109
            }
110

    
111
            _.each(this.vms, _.bind(function(vm){
112
                var html = '<li class="vm option options-object vm-{0}">' +
113
                           '<div class="options-object-cont">' +
114
                           '{2}' + 
115
                           '<span class="title">{1}</span>' + 
116
                           '<span class="value">{3}</span></div>' + 
117
                           '</li>';
118
                var el = $(html.format(vm.id, 
119
                                       util.truncate(_.escape(vm.get("name")), 23), 
120
                                       snf.ui.helpers.vm_icon_tag(vm, "small", {'class':'os'}),
121
                                       _.escape(vm.get_os())
122
                                      ))
123
                el.data({vm:vm,vm_id:vm.id})
124
                this.list.append(el);
125

    
126
                vm.bind("remove", function(){ el.remove()})
127
                vm.bind("change:name", function(i,v){el.find(".title").text(v)})
128
            }, this));
129
            
130
            this.init_handlers();
131
            this.set_selected();
132
        },
133

    
134
        set_selected: function() {
135
            _.each(this.selected, _.bind(function(el){
136
                this.vm(el).addClass("selected");
137
            }, this));
138
        },
139

    
140
        show_vms: function(network, vms, selected, callback) {
141
            this.network = network;
142
            this.reset();
143
            this.set_subtitle(network.escape("name"));
144
            this.vms = vms;
145
            this.selected = selected;
146
            this.cb = callback;
147
            this.show();
148
        },
149

    
150
        submit: function() {
151
            this.cb(this.get_selected());
152
        }
153
    })
154

    
155
    views.NetworkActionsView = views.View.extend({
156
        
157
        initialize: function(view, net, el, opts) {
158
            this.parent = view
159
            this.network = net;
160
            this.el = el;
161
            
162
            this.actions = this.$(".actions");
163
            this.selected = undefined;
164

    
165
            this.destroy = this.$(".actions .destroy a");
166
            this.connect = this.$(".actions .add");
167

    
168
            this.init_handlers();
169
            this.update_layout();
170
        },
171

    
172
        init_handlers: function() {
173
            this.connect.click(_.bind(function(e){
174
                e.preventDefault();
175
            }))
176
        },
177

    
178
        update_layout: function() {
179
        }
180
    });
181

    
182
    views.NetworkCreateView = views.Overlay.extend({
183
        view_id: "network_create_view",
184
        content_selector: "#networks-create-content",
185
        css_class: 'overlay-networks-create overlay-info',
186
        overlay_id: "network-create-overlay",
187

    
188
        title: "Create new private network",
189
        subtitle: "Networks",
190

    
191
        initialize: function(options) {
192
            views.NetworkCreateView.__super__.initialize.apply(this);
193

    
194
            this.create_button = this.$("form .form-action.create");
195
            this.text = this.$(".network-create-name");
196
            this.form = this.$("form");
197
            this.init_handlers();
198
        },
199

    
200
        init_handlers: function() {
201
            this.create_button.click(_.bind(function(e){
202
                this.submit();
203
            }, this));
204

    
205
            this.form.submit(_.bind(function(e){
206
                e.preventDefault();
207
                this.submit;
208
                return false;
209
            }, this))
210

    
211
            this.text.keypress(_.bind(function(e){
212
                if (e.which == 13) {this.submit()};
213
            },this))
214
        },
215

    
216
        submit: function() {
217
            if (this.validate()) {
218
                this.create();
219
            };
220
        },
221
        
222
        validate: function() {
223
            // sanitazie
224
            var t = this.text.val();
225
            t = t.replace(/^\s+|\s+$/g,"");
226
            this.text.val(t);
227

    
228
            if (this.text.val() == "") {
229
                this.text.closest(".form-field").addClass("error");
230
                this.text.focus();
231
                return false;
232
            } else {
233
                return true;
234
            }
235
        },
236

    
237
        create: function() {
238
            this.create_button.addClass("in-progress");
239
            snf.storage.networks.create(this.text.val(), _.bind(function(){
240
                this.hide();
241
            }, this));
242
        },
243

    
244
        beforeOpen: function() {
245
            this.create_button.removeClass("in-progress")
246
            this.text.closest(".form-field").removeClass("error");
247
            this.text.val("");
248
            this.text.show();
249
            this.text.focus();
250
        },
251

    
252
        onOpen: function() {
253
            this.text.focus();
254
        }
255
    });
256

    
257
    views.NetworkVMView = views.View.extend({
258

    
259
        initialize: function(vm, parent, firewall_controls, el) {
260
            this.firewall_controls = firewall_controls || false;
261
            this.vm = vm;
262
            // parent view di
263
            this.parent = parent;
264
            // TODO make it better
265
            this.el = el || this.parent.vm(vm);
266

    
267
            this.init_layout();
268
            this.update_layout();
269

    
270
            this.disconnect = this.$(".action-disconnect");
271
            this.confirm_el = this.$(".confirm_single");
272
            this.cancel = this.$("button.no");
273
            this.confirm = this.$("button.yes");
274
            this.details = this.$(".action-details");
275
            this.vm_connect = this.$(".machine-connect");
276

    
277
            this.init_handlers();
278
            this.connect_overlay = new views.VMConnectView();
279
            
280
            this.firewall_view = undefined;
281
            if (this.firewall_controls) {
282
                this.firewall_view = new views.FirewallEditView(this.vm, this.parent.network, this);
283
            }
284

    
285
        },
286
        
287
        init_handlers: function() {
288
            if (!this.parent.network.is_public()) {
289
                this.disconnect.click(_.bind(function(e){
290
                    e.preventDefault();
291
                    this.parent.network.get("actions").add("disconnect", this.vm.id);
292
                    this.parent.network.get("actions").remove("destroy");
293
                }, this));
294
                this.cancel.click(_.bind(function(e){
295
                    this.parent.network.get("actions").remove("disconnect", this.vm.id);
296
                    e.preventDefault()
297
                }, this));
298
                this.confirm.click(_.bind(function(e){
299
                    e.preventDefault()
300
                    this.disconnect_vm();
301
                    this.confirm_el.hide();
302
                    this.disconnect.removeClass("selected");
303
                }, this));
304

    
305
                snf.ui.main.bind("view:change", _.bind(function(v) {
306
                    if (v == "networks" ){ return }
307
                    this.confirm_el.hide();
308
                    this.disconnect.removeClass("selected");
309
                }, this));
310

    
311
                this.$(".remove-icon").click(_.bind(function(){
312
                    this.parent.network.get("actions").add("disconnect", this.vm.id);
313
                    this.parent.network.get("actions").remove("destroy");
314
                }, this));
315

    
316
                this.vm_connect.click(_.bind(function() {
317
                    this.connect_overlay.show(this.vm);
318
                }, this));
319
                
320
                this.parent.network.bind("change:actions", _.bind(function(model, action){
321
                    if (this.parent.network.get("actions").contains("disconnect", this.vm.id)) {
322
                        this.confirm_disconnect();
323
                    } else {
324
                        this.cancel_disconnect();
325
                    }
326
                }, this));
327
            }
328
            
329
            var vm = this.vm;
330
            this.details.click(function(){
331
                snf.ui.main.show_vm_details(vm);
332
            });
333

    
334
        },
335

    
336
        cancel_disconnect: function() {
337
            this.confirm_el.hide();
338
            this.disconnect.removeClass("selected");
339
            this.$(".net-vm-actions a").removeClass("visible");
340
        },
341

    
342
        confirm_disconnect: function() {
343
            this.confirm_el.show();
344
            this.disconnect.addClass("selected");
345
            this.$(".net-vm-actions a").addClass("visible");
346
        },
347

    
348
        init_layout: function() {
349
            if (!this.firewall_controls) { return };
350
        },
351

    
352
        update_layout: function() {
353
            this.$(".vm-name").text(snf.util.truncate(this.vm.get("name"), 40));
354
            this.$("img.logo").attr("src", ui.helpers.vm_icon_path(this.vm, "medium"));
355

    
356
            if (this.firewall_view) {
357
                this.$(".ipv4-text").text(this.vm.get_addresses().ip4);
358
                this.$(".ipv6-text").text(this.vm.get_addresses().ip6);
359
            }
360

    
361
            if (this.firewall_view) {
362
                this.firewall_view.update_layout();
363
            }
364
        },
365

    
366
        disconnect_vm: function() {
367
            this.$("a.selected").removeClass("selected");
368
            this.parent.network.remove_vm(this.vm);
369
        },
370

    
371
        update_firewall_layout: function() {
372
        }
373

    
374

    
375
    })
376

    
377
    views.NetworkModelRenameView = views.View.extend({
378
        initialize: function(parent, network) {
379
            this.parent = parent;
380
            this.network = network;
381
            this.el = this.parent.el.find(".name-div");
382

    
383
            this.icon = this.$(".rename-network");
384
            this.save = this.$("span.save");
385
            this.cancel = this.$("span.cancel");
386
            this.buttons = this.$(".editbuttons");
387
            this.name = this.$("span.name");
388
            this.editing = false;
389
            this.init_handlers();
390
            this.update_layout();
391
        },
392

    
393
        init_handlers: function() {
394
            this.icon.click(_.bind(function(){
395
                this.editing = true;
396
                this.update_layout();
397
            }, this));
398
            this.cancel.click(_.bind(function(){
399
                this.editing = false;
400
                this.update_layout();
401
            }, this));
402
            this.save.click(_.bind(function(){
403
                this.submit();
404
            }, this))
405
        },
406
        
407
        submit: function() {
408
            var value = _(this.input.val()).trim();
409
            if (value == "") { return }
410

    
411
            this.network.rename(value, _.bind(function(){
412
                this.editing = false;
413
                this.update_layout();
414
            }, this));
415
        },
416

    
417
        create_input: function() {
418
            this.input = $('<input type="text" class="network-rename-input" />');
419
            this.input.val(this.network.get("name"));
420
            this.el.append(this.input);
421
            this.input.focus();
422
            this.input.bind("keydown", _.bind(function(ev){
423
                ev.keyCode = ev.keyCode || ev.which;
424
                if (ev.keyCode == 13) { this.submit(); };
425
                if (ev.keyCode == 27) {this.editing = false; this.update_layout()};
426
            }, this));
427
        },
428

    
429
        remove_input: function() {
430
            if (!this.input) { return }
431
            this.input.remove();
432
        },
433

    
434
        update_layout: function() {
435
            if (this.editing) {
436
                if (this.buttons.is(":visible")) { return }
437
                this.icon.hide();
438
                this.buttons.show();
439
                this.create_input();
440
                this.name.hide();
441
            } else {
442
                this.buttons.hide();
443
                this.remove_input();
444
                this.name.show();
445
                this.icon.show();
446
            }
447
        }
448
    })
449

    
450
    views.FirewallEditView = views.View.extend({
451
        initialize: function(vm, network, parent) {
452
            this.parent = parent;
453
            this.vm = vm;
454
            this.network = network;
455
            this.el = this.parent.el;
456

    
457
            views.FirewallEditView.__super__.initialize.apply(this);
458

    
459
            // elements
460
            this.toggler = this.$(".firewall-toggle");
461
            this.indicator = this.$(".machines-label span");
462
            this.progress = this.$(".network-progress-indicator");
463
            this.content = this.$(".firewall-content");
464
            this.inputs = this.$("input[type=radio]");
465
            this.labels = this.$("span.checkbox-legends, label.checkbox-legends");
466
            this.apply = this.$(".firewall-apply");
467

    
468
            this.$(".firewall-content").hide();
469
            this.$(".firewall-content input[type=radio]").attr("name", "firewall-opt-for-{0}".format(this.vm.id))
470
            var mode = this.vm.firewall_profile(this.network.id);
471
            this.$(".firewall-content input[value={0}]".format(mode)).attr("checked", true);
472

    
473
            this.init_handlers();
474
            this.update_layout();
475
        },
476
        
477
        _get_selected: function() {
478
            return this.inputs.filter(":checked");
479
        },
480

    
481
        reset_selected: function() {
482
        },
483

    
484
        submit: function() {
485
        },
486

    
487
        reset_value: function() {
488
            this.inputs.filter("[value={0}]".format(this.vm.firewall_profile(this.network.id))).attr("checked");
489
        },
490

    
491
        init_handlers: function() {
492
            this.toggler.click(_.bind(function(){
493
                cont = this.content;
494
                if (cont.is(":visible")) {
495
                    this.hide_firewall();
496
                    this.reset_value();
497
                } else {
498
                    this.show_firewall();
499
                }
500

    
501
                $(window).trigger("resize");
502
            }, this))
503
            
504
            this.apply.click(_.bind(function(){
505
                this.apply.addClass("in-progress");
506
                
507
                // make the api call
508
                this.vm.set_firewall(this.network.id, this.value(), 
509
                // complete
510
                _.bind(function() {
511
                    // complete callback
512
                    this.apply.removeClass("in-progress");
513
                }, this), 
514
                // error
515
                _.bind(function(){
516
                    this.vm.remove_pending_firewall(this.network.id, this.value());
517
                }, this));
518
                this.hide_firewall();
519
            }, this))
520

    
521
            this.inputs.change(_.bind(function(){
522
                this.update_selected();
523
            }, this))
524
            
525
            var self = this;
526
            this.$(".checkbox-legends").click(function(el) {
527
                var el = $(this);
528
                el.prev().click();
529
                self.update_selected();
530
            })
531
        },
532

    
533
        update_selected: function() {
534
            this.update_layout();
535
        },
536

    
537
        show_firewall: function() {
538
            this.content.slideDown(100, function(){$(window).trigger("resize")});
539
            this.toggler.addClass("open");
540
        },
541

    
542
        hide_firewall: function() {
543
            this.content.slideUp(100, function(){$(window).trigger("resize")});
544
            this.toggler.removeClass("open");
545
        },
546

    
547
        value: function() {
548
            return this._get_selected().val();
549
        },
550

    
551
        update_layout: function() {
552
            if (this.value() == this.vm.firewall_profile(this.network.id)) {
553
                this.apply.hide();
554
            } else {
555
                this.apply.show();
556
            }
557

    
558
            profile = this.vm.firewall_profile(this.network.id);
559
            if (this.vm.has_firewall(this.network.id)) {
560
                this.$(".firewall-toggle .label span").text("On");
561
                this.$(".firewall-toggle .label span").removeClass("firewall-off");
562
                this.$(".firewall-toggle .label span").addClass("firewall-on");
563
            } else {
564
                this.$(".firewall-toggle .label span").text("Off");
565
                this.$(".firewall-toggle .label span").removeClass("firewall-on");
566
                this.$(".firewall-toggle .label span").addClass("firewall-off");
567
            }
568
            
569
            this.$("span.checkbox-legends").removeClass("current");
570
            this.inputs.filter("[value={0}]".format(this.vm.firewall_profile(this.network.id))).next().addClass("current");
571

    
572
            var firewalling = this.vm.firewall_pending(this.network.id);
573
            var el = this.el;
574
            
575
            if (firewalling) {
576
                el.find("button").addClass("in-progress").show();
577
                el.find(".network-progress-indicator").show();
578
            } else {
579
                el.find("button").removeClass("in-progress");
580
                el.find(".network-progress-indicator").hide();
581
            }
582
        }
583
    })
584

    
585
    views.NetworkModelView = views.View.extend({
586
        
587
        firewall: false,
588

    
589
        initialize: function(network, view) {
590
            this.parent_view = view;
591
            this.network = network;
592
            this.is_public = network.is_public();
593

    
594
            this.init_vm_handlers();
595

    
596
            this.view_id = "networks_view_" + network.id;
597
            views.NetworkModelView.__super__.initialize.call(this);
598

    
599
            this.vm_views = {};
600

    
601
            this.el = this.create_el();
602

    
603
            // element helpers
604
            this.vms_list = this.$(".machines-list");
605
            this.vms_list_toggler = this.$(".list-toggle");
606
            
607
            this.init_handlers();
608
            this.update_vms();
609
            this.update_layout();
610

    
611
            this.hide_vm_list();
612
            this.vms_list.hide();
613

    
614
            this.rename_view = undefined;
615
            if (!this.network.is_public()) {
616
                this.rename_view = new views.NetworkModelRenameView(this, network);
617
            }
618
        },
619

    
620
        show_vm_list: function() {
621
            if (this.vms_empty()) { return }
622
            this.vms_list_toggler.addClass("open");
623
            this.vms_list.slideDown(function(){
624
                $(window).trigger("resize");
625
            }).closest(".network").addClass("expand");
626
            this.$(".empty-network-slot").show();
627
            this.vms_visible = true;
628
        },
629

    
630
        hide_vm_list: function() {
631
            this.vms_list_toggler.removeClass("open");
632
            this.vms_list.slideUp(function(){
633
                $(window).trigger("resize");
634
            }).closest(".network").removeClass("expand");
635
            this.$(".empty-network-slot").hide();
636
            this.vms_visible = false;
637
        },
638
        
639
        // fix left border position
640
        fix_left_border: function() {
641
            if (!this.vms_visible) { return };
642
            
643
            var imgheight = 2783;
644
            var opened_vm_height = 133 + 20;
645
            var closed_vm_height = 61 + 20;
646
            var additional_height = 25;
647

    
648
            if (!this.is_public) { 
649
                imgheight = 2700;
650
                additional_height = 65;
651
            };
652
            
653
            var contents = this.$(".network-contents");
654
            var last_vm = this.$(".network-machine:last .cont-toggler.open").length;
655
            var last_vm_height = closed_vm_height;
656
            if (last_vm > 0){
657
                last_vm_height = opened_vm_height;
658
            }
659

    
660
            var vms_opened = this.$(".network-machine .cont-toggler.open").length;
661
            var vms_closed = this.$(".network-machine").length - vms_opened;
662

    
663
            var calc_height = (vms_opened * opened_vm_height) + (vms_closed * closed_vm_height) + additional_height; 
664
            var bgpos = imgheight - calc_height + last_vm_height - 30;
665
            this.$(".network-contents").css({'background-position':'33px ' + (-bgpos) + 'px'});
666
        },
667

    
668

    
669
        init_handlers: function() {
670
            var self = this;
671

    
672
            this.vms_list_toggler.click(_.bind(function(){
673
                if (this.vms_list.is(":visible")) {
674
                    this.hide_vm_list();
675
                } else {
676
                    this.fix_left_border();
677
                    this.show_vm_list();
678
                }
679

    
680
                this.check_empty_vms();
681
            }, this));
682

    
683
            this.$(".action-add").click(_.bind(function(e){
684
                e.preventDefault();
685
                this.network.get("actions").remove("destroy");
686
                this.show_connect_vms();
687
            }, this))
688

    
689
            this.$(".add-icon").click(_.bind(function(e){
690
                e.preventDefault();
691
                this.show_connect_vms();
692
            }, this))
693

    
694
            this.$(".net-actions .destroy a").click(_.bind(function(e){
695
                e.preventDefault();
696
                self.network.get("actions").add("destroy");
697
                self.network.get("actions").remove_all("disconnect");
698
            }, this));
699

    
700
            self.network.bind("change:actions", _.bind(function(net, action) {
701
                if (this.network.get("actions").contains("destroy")) {
702
                    this.confirm_destroy();
703
                } else {
704
                    this.cancel_destroy();
705
                }
706
            }, this))
707

    
708
            this.$(".net-actions button.no").click(function(e){
709
                e.preventDefault();
710
                self.network.get("actions").remove("destroy");
711
            });
712

    
713
            this.$(".net-actions button.yes").click(function(e){
714
                e.preventDefault();
715
                var el = $(this);
716
                el.closest(".confirm_single").hide();
717
                el.parent().parent().find(".selected").removeClass("selected");
718
                self.network.call('destroy', {}, function(){
719
                    el.closest(".confirm_single").removeClass("in-progress");
720
                });
721
                el.closest(".confirm_single").addClass("in-progress");
722
            });
723

    
724
            snf.ui.main.bind("view:change", _.bind(function(v) {
725
                if (v == "networks" ){ return }
726
                this.$(".confirm_single").hide();
727
                this.$("a.selected").removeClass("selected");
728
            }, this));
729

    
730
            $(window).bind("resize", _.bind(function() {
731
                this.fix_left_border();
732
            }, this));
733
        },
734

    
735
        show_connect_vms: function() {
736
            this.$(".confirm_single").hide();
737
            this.$("a.selected").removeClass("selected");
738
            var vms = this.network.get_connectable_vms();
739
            this.parent_view.connect_machines_view.show_vms(this.network,
740
                                                            vms, [], 
741
                                                            _.bind(this.connect_vms, this));
742
        },
743

    
744
        cancel_destroy: function() {
745
            this.$(".net-actions .destroy .confirm_single").hide();
746
            this.$(".net-actions .destroy a.selected").removeClass("selected");
747
            this.$(".net-actions a").removeClass("visible");
748
        },
749

    
750
        confirm_destroy: function() {
751
            this.$(".destroy .confirm_single").show();
752
            this.$(".destroy a").addClass("selected");
753
            this.$(".net-actions a").addClass("visible");
754
        },
755

    
756
        connect_vms: function(vms) {
757
            _.each(vms, _.bind(function(vm){
758
                this.network.add_vm(vm);
759
            }, this));
760

    
761
            this.parent_view.connect_machines_view.hide();
762
        },
763

    
764
        create_el: function() {
765
            var el = this.$(this.tpl).clone().attr("id", "network-" + this.network.id)
766
            return el;
767
        },
768

    
769
        init_vm_handlers: function() {
770
            storage.vms.bind("add", _.bind(this.vm_added_handler, this, "add"));
771
            storage.vms.bind("network:connect", _.bind(this.vm_changed_handler, this, "connect"));
772
            storage.vms.bind("change", _.bind(this.vm_changed_handler, this, "change"));
773
            storage.vms.bind("reset", _.bind(this.vm_changed_handler, this, "reset"));
774
            storage.vms.bind("remove", _.bind(this.vm_removed_handler, this, "remove"));
775
            storage.vms.bind("network:disconnect", _.bind(this.vm_removed_handler, this, "disconnect"));
776
        },
777

    
778
        get_vm_id: function(vm) {
779
            return this.vm_id_tpl.format(this.network.id, vm.id)
780
        },
781

    
782
        vm: function(vm) {
783
            return $(this.get_vm_id(vm))
784
        },
785

    
786
        vm_added_handler: function(action, vm) {
787
            if (!this.network.contains_vm(vm)) { return }
788
            this.add_or_update_vm(vm);
789
            this.update_layout();
790
            this.fix_left_border();
791
        },
792

    
793
        vm_changed_handler: function(action, vms, model, changes) {
794
            var vms = vms || [];
795
            // reset or change
796
            if (action == "reset") {
797
                vms = vms;
798
            } else {
799
                if (!_.isArray(vms)) {
800
                    vms = [vms]
801
                }
802
            }
803

    
804
            if (action == "connect") {
805
                vms = [model];
806
            }
807
            
808
            _.each(vms, _.bind(function(vm) {
809
                if (!this.network.contains_vm(vm)) { return }
810
                this.add_or_update_vm(vm);
811
            }, this));
812
            this.update_layout();
813
        },
814

    
815
        vm_removed_handler: function(action, vm, model) {
816
            if (action == "disconnect") { vm = model };
817
            this.fix_left_border();
818
            this.remove_vm(vm);
819
            this.update_layout();
820
        },
821

    
822
        remove_vm: function(vm) {
823
            if (this.vm(vm).length) {
824
                this.vm(vm).remove();
825
                try {
826
                    delete this.vm_views[vm.id]
827
                } catch (err) {
828
                }
829
            }
830
        },
831
        
832
        create_vm: function(vm) {
833
            vm_el = $(this.vm_tpl).clone().attr({id:this.get_vm_id(vm).replace("#","")});
834
            this.vms_list.append(vm_el);
835
            this.post_vm_add(vm);
836

    
837
            if (!this.vm_views[vm.id]) {
838
                vm_view = this.vm_views[vm.id] = new views.NetworkVMView(vm, this, this.firewall, vm_el);
839
            }
840
        },
841

    
842
        add_or_update_vm: function(vm) {
843
            if (!vm || !this.network.contains_vm(vm)) { return };
844

    
845
            var vm_el = this.vm(vm);
846
            var vm_view = this.vm_views[vm.id];
847

    
848
            if (vm_el.length == 0) {
849
                this.create_vm(vm);
850
            }
851
            
852
            if (vm_view) { vm_view.update_layout() };
853

    
854
            this.update_vm(vm);
855
            this.post_vm_update(vm);
856
        },
857
        update_vm: function(vm){},
858
        post_vm_add: function(vm){},
859
        post_vm_update: function(vm){},
860

    
861
        update_vms: function(vms) {
862
            if (!vms) { vms = this.network.vms.list() };
863
            _.each(vms, _.bind(function(vm){
864
                this.add_or_update_vm(vm);
865
            }, this));
866
        },
867

    
868
        check_empty_vms: function() {
869
            if (this.network.vms.get().length == 0) {
870
                this.hide_vm_list();
871
            }
872
        },
873

    
874
        vms_empty: function() {
875
            return this.network.vms.get().length == 0;
876
        },
877

    
878
        remove: function() {
879
            $(this.el).remove();
880
        },
881

    
882
        update_layout: function() {
883
            // has vms ???
884
            this.check_empty_vms(this.network.vms.list());
885

    
886
            // is expanded ???
887
            //
888
            // whats the network status ???
889
            //
890
            this.$(".machines-count").text(this.network.vms.get().length);
891

    
892
            var net_name = this.network.get("name");
893
            if (net_name == "public") { net_name = "Internet" }
894
            this.$(".name-div span.name").text(net_name);
895

    
896
            if (this.rename_view) {
897
                this.rename_view.update_layout();
898
            }
899
            
900
            this.$(".net-status").text(this.network.state_message());
901

    
902
            if (this.network.in_progress())  {
903
                this.$(".spinner").show();
904
                this.$(".network-indicator").addClass("in-progress");
905
            } else {
906
                this.$(".spinner").hide();
907
                this.$(".network-indicator").removeClass("in-progress");
908
            }
909

    
910
            if (this.network.get("state") == "DESTROY") {
911
                this.$(".spinner").show();
912
                this.$(".state").addClass("destroying-state");
913
            }
914
        }
915
    })
916

    
917
    views.PublicNetworkView = views.NetworkModelView.extend({
918
        firewall: true,
919
        tpl: "#public-template",
920
        vm_tpl: "#public-machine-template",
921
        vm_id_tpl: "#network-{0}-vm-{1}",
922

    
923
        update_vm: function(vm) {
924
        }
925
    })
926
    
927
    views.PrivateNetworkView = views.NetworkModelView.extend({
928
        tpl: "#private-template",
929
        vm_tpl: "#private-machine-template",
930
        vm_id_tpl: "#network-{0}-vm-{1}"
931
    })
932

    
933
    views.NetworksView = views.View.extend({
934
        
935
        view_id: "networks",
936
        pane: "#networks-pane",
937
        el: "#networks-pane",
938

    
939
        initialize: function() {
940
            // elements shortcuts
941
            this.create_cont = this.$("#networks-createcontainer");
942
            this.container = this.$("#networks-container");
943
            this.public_list = this.$(".public-networks");
944
            this.private_list = this.$(".private-networks");
945

    
946
            views.NetworksView.__super__.initialize.call(this);
947
            this.init_handlers();
948
            this.network_views = {};
949
            this.update_networks(storage.networks.models);
950

    
951
            this.create_view = new views.NetworkCreateView();
952
            this.connect_machines_view = new views.NetworkConnectVMsOverlay();
953

    
954
        },
955
        
956
        exists: function(net) {
957
            return this.network_views[net.id];
958
        },
959

    
960
        add_or_update: function(net) {
961
            var nv = this.exists(net);
962
            if (!nv) {
963
                nv = this.create_network_view(net)
964
                this.network_views[net.id] = nv;
965
                
966
                if (net.is_public()) {
967
                    this.public_list.append(nv.el);
968
                    this.public_list.show();
969
                } else {
970
                    this.private_list.append(nv.el);
971
                    this.private_list.show();
972
                }
973
            }
974

    
975
            // update vms
976
            // for cases where network servers list
977
            // get updated after vm addition and
978
            // vm_added_handler fails to append the
979
            // vm to the list
980
            nv.update_vms();
981
            nv.update_layout();
982
        },
983
        
984
        create_network_view: function(net) {
985
            if (net.is_public()) {
986
                return new views.PublicNetworkView(net, this);
987
            }
988
            return new views.PrivateNetworkView(net, this);
989
        },
990
        
991
        init_handlers: function() {
992
            storage.networks.bind("add", _.bind(this.network_added_handler, this, "add"));
993
            storage.networks.bind("change", _.bind(this.network_changed_handler, this, "change"));
994
            storage.networks.bind("reset", _.bind(this.network_changed_handler, this, "reset"));
995
            storage.networks.bind("remove", _.bind(this.network_removed_handler, this, "remove"));
996

    
997
            this.$("#networkscreate").click(_.bind(function(e){
998
                e.preventDefault();
999
                this.create_view.show();
1000
            }, this));
1001
            
1002
        },
1003

    
1004
        update_networks: function(nets) {
1005
            _.each(nets, _.bind(function(net){
1006
                if (net.get("status") == "DELETED") { return }
1007
                view = this.add_or_update(net);
1008
            }, this));
1009
        },
1010

    
1011
        show: function() {
1012
            this.container.show();
1013
            $(this.el).show();
1014
        },
1015

    
1016
        network_added_handler: function(type, net) {
1017
            this.update_networks([net]);
1018
        },
1019

    
1020
        network_changed_handler: function(type, models) {
1021
            var nets = [];
1022
            if (type == "change") {
1023
                nets = [models]
1024
            } else {
1025
                nets = models.models;
1026
            }
1027

    
1028
            this.update_networks(nets)
1029
        },
1030

    
1031
        network_removed_handler: function(type, net) {
1032
            this.remove_net(net)
1033
            if (this.private_list.find(".network").length == 0) {
1034
                this.private_list.hide();
1035
            }
1036
        },
1037

    
1038
        network_added: function(net) {
1039
            return this.network_views[net.id];
1040
        },
1041

    
1042
        get_network_view: function(net) {
1043
            return this.network_views[net.id];
1044
        },
1045

    
1046
        remove_net: function(net) {
1047
            if (this.network_added(net)) {
1048
                var view = this.get_network_view(net);
1049
                view.remove();
1050
                delete this.network_views[net.id];
1051
            }
1052
        },
1053

    
1054
        __update_layout: function() {
1055
        }
1056
    });
1057

    
1058
})(this);