Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / static / snf / js / ui / web / ui_icon_view.js @ 21387cf4

History | View | Annotate | Download (32.9 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
    // handle extended info toggler
53
    views.VMActionErrorView = views.View.extend({
54
    
55
        initialize: function (vm, view) {
56
            this.vm = vm;
57
            this.view = view;
58
            this.vm_view = this.view.vm(vm);
59

    
60
            this.has_error = false;
61
            
62
            this.error = this.vm_view.find(".action-error");
63
            this.close = this.vm_view.find(".close-action-error");
64
            this.show_btn = this.vm_view.find(".show-action-error");
65

    
66
            this.init_handlers();
67
            this.update_layout();
68
        },
69

    
70
        init_handlers: function() {
71
            // action call failed notify the user
72
            this.vm.bind("action:fail", _.bind(function(args){
73
                if (this.vm.action_error) {
74
                    this.has_error = true;
75
                    var action = "undefined";
76
                    try {
77
                        action = _.last(args).error_params.extra_details['Action'];
78
                    } catch (err) {console.log(err)};
79
                    
80
                    this.error.find(".action").text(action);
81
                    this.error.show();
82
                }
83
            }, this));
84
            
85
            // show error overlay
86
            this.show_btn.click(_.bind(function() {
87
                if (this.vm.action_error) {
88
                    this.show_error_overlay(this.vm.action_error);
89
                }
90
                this.vm.reset_action_error();
91
            }, this));
92
            
93
            // user requests to forget about the error
94
            this.close.click(_.bind(_.bind(function() {
95
                this.error.hide();
96
                this.vm.reset_action_error();
97
            }, this)));
98
            
99
            // hide error message if action fail get reset
100
            this.vm.bind("action:fail:reset", _.bind(function(){
101
                this.error.hide();
102
            }, this));
103
        },
104

    
105
        show_error_overlay: function(args) {
106
            var args = util.parse_api_error.apply(util, args);
107
            
108
            // force logout if UNAUTHORIZED request arrives
109
            if (args.code == 401) { snf.ui.logout(); return };
110
            
111
            var error_entry = [args.ns, args.code, args.message, '', args.type, args.details, args];
112
            ui.main.error_view.show_error.apply(ui.main.error_view, error_entry);
113
        },
114

    
115
        update_layout: function() {
116
            if (this.vm.action_error) {
117
                this.error.show();
118
            }
119
        }
120
    });
121

    
122
    // handle extended info toggler
123
    views.IconInfoView = views.View.extend({
124
    
125
        initialize: function (vm, view) {
126
            this.vm = vm;
127
            this.view = view;
128
            this.vm_view = this.view.vm(vm);
129
            
130
            this.info_toggle = $(".cont-toggler-wrapper.info .toggler", this.vm_view);
131
            this.ips_toggle = $(".cont-toggler-wrapper.ips .toggler", this.vm_view);
132
            this.info_el = $("div.info-content.vm-info", this.vm_view);
133
            this.ips_el = $("div.info-content.ips", this.vm_view);
134
            this.label = $(".label", this.vm_view);
135

    
136
            this.set_handlers();
137
        },
138

    
139
        set_handlers: function() {
140
            this.info_toggle.click(_.bind(function(){
141
                this.ips_el.slideUp();
142
                this.ips_toggle.removeClass("open");
143

    
144
                this.info_el.slideToggle();
145
                this.view.vm(this.vm).toggleClass("light-background");
146

    
147
                if (this.info_toggle.hasClass("open")) {
148
                    this.info_toggle.removeClass("open");
149
                    this.vm.stop_stats_update();
150
                } else {
151
                    this.info_toggle.addClass("open");
152
                    this.view.details_views[this.vm.id].update_layout();
153
                    this.view.tags_views[this.vm.id].update_layout();
154
                    this.view.stats_views[this.vm.id].update_layout();
155
                }
156
                
157
                var self = this;
158
                window.setTimeout(function() {$(self.view).trigger("resize")}, 300);
159
            }, this));
160

    
161
            this.ips_toggle.click(_.bind(function(){
162
                if(this.ips_toggle.parent().hasClass("disabled")) {
163
                  return;
164
                }
165
                this.info_el.slideUp();
166
                this.info_toggle.removeClass("open");
167

    
168
                this.ips_el.slideToggle();
169
                this.view.vm(this.vm).toggleClass("light-background");
170
                var self = this;
171
                if (this.ips_toggle.hasClass("open")) {
172
                    this.ips_toggle.removeClass("open");
173
                } else {
174
                    this.ips_toggle.addClass("open");
175
                }
176
                window.setTimeout(function() {$(self.view).trigger("resize")}, 300);
177
            }, this));
178

    
179
            this.vm_view.find(".stats-report").click(_.bind(function(e){
180
                e.preventDefault();
181
                snf.ui.main.show_vm_details(this.vm);
182
            }, this)).attr("href", "#machines/single/details/{0}".format(this.vm.id));
183
        }
184
    
185
    })
186

    
187
    // rename handler view
188
    // only icon view contains rename capability
189
    views.IconRenameView = views.View.extend({
190
        
191
        initialize: function(vm, view) {
192
            this.vm = vm;
193
            this.view = view;
194
            // name container
195
            this.el = $('div#' + this.view.id_tpl + vm.id + " div.name").get(0);
196
            // name inline element
197
            this.name = this.$('span.name');
198
            // rename button
199
            this.rename = this.$('span.rename');
200
            // save button
201
            this.save = this.$('.save');
202
            // cancel rename button
203
            this.cancel = this.$('.cancel');
204
            // where to place the input field
205
            this.edit_cont = this.$(".namecontainer");
206
            // buttons container
207
            this.buttons = this.$(".editbuttons");
208
            // current state
209
            this.renaming = false;
210
            // init event handlers
211
            this.set_handlers();
212
            // paint
213
            this.update_layout();
214
            views.IconRenameView.__super__.initialize.call(this);
215
        },
216
        
217
        // update elements visibility/state
218
        update_layout: function() {
219
            // if in renaming state
220
            if (this.renaming) {
221
                // if name is hidden we are already in renaming state
222
                // dont do nothing
223
                if (this.name.is(":hidden")){return}
224
                
225
                // hide name element to make space for the 
226
                // text input
227
                this.name.hide();
228
                this.rename.hide();
229
                // show confirm/cancel buttons
230
                this.buttons.show();
231
                // create text element
232
                this.create_input();
233
            } else {
234
                // name is visible not in edit mode
235
                if (this.name.is(":visible")){return}
236

    
237
                this.name.show();
238
                this.rename.show();
239
                this.buttons.hide();
240
                this.remove_input();
241
            }
242
        },
243
        
244
        // create rename input field and set appropriate 
245
        // event handlers
246
        create_input: function() {
247
            var self = this;
248
            this.edit_cont.append('<input class="vm-rename nametextbox" type="text" />');
249
            this.$('input').val(this.vm.get('name'));
250
            // give edit focus
251
            this.$('input').focus();
252
            // handle enter press
253
            this.$('input').keydown(function(ev){
254
                ev.keyCode = ev.keyCode || ev.which;
255
                if (ev.keyCode == 13) { self.submit(); }
256
                if (ev.keyCode == 27) { self.renaming = false; self.update_layout(); }
257
            })
258
        },
259
        
260
        // remove input element
261
        remove_input: function() {
262
            this.$('input').remove();
263
        },
264
        
265
        // initialize event handlers
266
        set_handlers: function() {
267
            var self = this;
268
            // start rename when rename button is pressed
269
            this.rename.click(function() {
270
                self.renaming = true;
271
                self.update_layout();
272
            });
273
            
274
            // double click on name
275
            $(this.el).dblclick(function() {
276
                self.renaming = true;
277
                self.update_layout();
278
            });
279

    
280
            // cancel rename
281
            this.cancel.click(function() {
282
                self.renaming = false;
283
                self.update_layout();
284
            })
285
            
286
            // apply the rename
287
            // TODO: check if name is equal than the previous value
288
            this.save.click(function() {
289
                self.submit();
290
            })
291
        },
292

    
293
        submit: function() {
294
            var value = _(self.$('input').val()).trim();
295
            if (value == "") { return };
296
            this.renaming = false;
297
            this.vm.rename(self.$('input').val());
298
            this.update_layout();
299
        }
300
    });
301
    
302
    // VM connect interaction view
303
    views.IconVMConnectView = views.View.extend({
304
        
305
        initialize: function(vm, view) {
306
            // parent view (single, icon, list)
307
            this.parent = view;
308
            this.vm = vm;
309
            this.el = view.vm(vm);
310
            this.set_handlers();
311
            views.IconVMConnectView.__super__.initialize.call(this);
312
        },
313
        
314
        // set the appropriate handlers
315
        set_handlers: function() {
316
            // setup connect handler on vm icon interaction
317
            var el = this.el;
318
            var vm = this.vm;
319

    
320
            // element that triggers the connect handler
321
            var connect = el.find("div.connect-arrow, .logo");
322
            // connect status handler
323
            var handler = _.bind(this.connect_handler, {vm:vm, el:el, view:this.parent});
324
            $(connect).bind({'mouseover': handler, 'mouseleave': handler, 
325
                            'mousedown': handler, 'mouseup': handler,
326
                            'click': handler });
327
            
328
            // setup connect arrow display handlers 
329
            // while hovering vm container
330
            el.bind("mouseover", function(){
331
                if (vm.is_connectable()) {
332
                    el.find(".connect-border").show();
333
                    el.find(".connect-arrow").show();
334
                    el.find(".logo").css({cursor:"pointer"});
335
                } else {
336
                    el.find(".connect-border").hide();
337
                    el.find(".connect-arrow").hide();
338
                    el.find(".logo").css({cursor: "default"});
339
                }
340
            }).bind("mouseleave", function(){
341
                el.find(".connect-border").hide();
342
                el.find(".connect-arrow").hide();
343
            });
344
        },
345
        
346
        // connect arrow interaction handlers
347
        // BEWARE, this function has different context
348
        // than the View object itself, see set_vm_handlers
349
        connect_handler: function(event) {
350
            // nothing to do if we cannot connect to the vm
351
            if (!this.vm.is_connectable()) {return}
352
            
353
            var logo = this.el.find(".logo");
354
            var arrow = this.el.find(".connect-arrow");
355
            var border = this.el.find(".connect-border");
356
            
357
            // clear icon states
358
            logo.removeClass('single-image-state1 single-image-state2 single-image-state3 single-image-state4');
359
            
360
            // append the appropriate state class
361
            switch (event.type) {
362
                case "mouseover":       
363
                    logo.addClass('single-image-state4');
364
                    arrow.addClass('border-hover');
365
                    break;
366
                
367
                case "mouseleave":
368
                    logo.addClass('single-image-state1');
369
                    arrow.removeClass('border-hover');
370
                    break;
371

    
372
                case "mouseup":
373
                    logo.addClass('single-image-state4');
374
                    //this.view.connect_overlay.show(this.vm);
375
                    break;
376

    
377
                case "mousedown":
378
                    logo.addClass('single-image-state2');
379
                    break;
380

    
381
                case "click":
382
                    //logo.addCLass('single-image-state4');
383
                    //this.view.connect_to_console(vm);
384
                    this.view.connect_overlay.show(this.vm);
385
                    break;
386

    
387
                default:
388
                    ;
389
            }
390
        },
391
        
392
        update_layout: function() {
393
        }
394

    
395
    });
396
    
397
    // vm metadata subview for icon and single view
398
    views.VMTagsView = views.View.extend({
399
        view_id: 'vm_tags',
400
        // metadata container selector
401
        el_sel: '.vm-metadata',
402
        // metadata row template
403
        tag_tpl: '<span class="tag-item"><span class="key">{0}</span><span class="value">{1}</span></span>',
404
        // how many tags to show
405
        tag_limit: 4,
406
        // truncate options (because container has different size on icon/single views)
407
        tag_key_truncate: 7,
408
        tag_value_truncate: 15,
409

    
410
        initialize: function(vm, view, toggle, limit, tag_key_truncate, tag_value_truncate) {
411
            this.tag_limit = limit || this.tag_limit;
412

    
413
            this.tag_key_truncate = tag_key_truncate || this.tag_key_truncate;
414
            this.tag_value_truncate = tag_value_truncate || this.tag_value_truncate;
415

    
416
            // does the view toggles the metadata container (single view)
417
            this.toggle = toggle || false;
418
            // parent view
419
            this.parent = view;
420
            this.vm = vm;
421
            this.el = this.parent.vm(vm);
422
            this.view_id = this.view_id + "_" + vm.id;
423

    
424
            // link to raise the metadata manager overlay
425
            this.link = this.$('a.manage-metadata');
426

    
427
            views.VMTagsView.__super__.initialize.call(this);
428
            this.set_handlers();
429
            this.update_layout();
430
        },
431
        
432
        // set the appropriate handlers
433
        set_handlers: function() {
434
            var self = this;
435
            // show the metadata editor overlay
436
            this.link.click(_.bind(function(ev) {
437
                ev.preventDefault();
438
                this.parent.metadata_view.show(this.vm);
439
            }, this));
440

    
441
            // tags/ips have show/hide control ? bind events for them
442
            var self = this;
443
            if (this.toggle) {
444
                $(this.el).find(".tags-header").click(_.bind(function(){
445
                    $(self.el).find(".tags-content").slideToggle(600);
446
                    var details_toggler = $(this.el).find(".tags-header " +
447
                                                          ".cont-toggler");
448
                    if (details_toggler.hasClass("open")) {
449
                        details_toggler.removeClass("open");
450
                    } else {
451
                        details_toggler.addClass("open");
452
                    }
453
                }, this));
454
                $(this.el).find(".tags-header").find(".toggler").removeClass("open");
455
                $(self.el).find(".tags-content").hide();
456
            }
457
        },
458
        
459
        // update metadata container and data
460
        update_layout: function() {
461

    
462
            // api metadata object
463
            var meta =  this.vm.get('metadata');
464

    
465
            var i = 0;
466
            var cont = $(this.el).find(".items");
467

    
468
            // clear existing items
469
            cont.find(".tag-item").remove();
470
            
471
            // create tag elements
472
            _.each(meta, function(value, key){
473
                // respect the limit
474
                if (i > this.tag_limit) {
475
                    return;
476
                }
477
                
478
                // create element
479
                var _key = _.escape(util.truncate(key, this.tag_key_truncate));
480
                var _value = _.escape(util.truncate(": " + value, this.tag_value_truncate));
481
                var new_el = $(this.tag_tpl.format(_key, _value));
482

    
483
                // add title attributes, improve accesibility
484
                // truncated values
485
                new_el.find("span.key").attr("title", key);
486
                new_el.find("span.value").attr("title", _.escape(value));
487

    
488
                cont.append(new_el);
489
            }, this);
490
        }
491
    });
492
    
493

    
494
    // stats subview for single/icon views
495
    views.VMStatsView = views.View.extend({
496

    
497
        initialize: function(vm, view, options) {
498
            if (!options) {options = {}};
499
            this.vm = vm;
500
            this.parent = view;
501
            this.sel = options.el || this.el_sel || ".lower";
502
            this.el = this.parent.vm(vm).find(this.sel);
503
            this.selected_stats_period = 'hourly';
504
            
505
            // elements shortcuts
506
            this.cpu_loading = this.el.find(".cpu-graph .stat-busy");
507
            this.cpu_error = this.el.find(".cpu-graph .stat-error");
508
            this.cpu_img = this.el.find(".cpu-graph .stat-img");
509
            this.net_loading = this.el.find(".network-graph .stat-busy");
510
            this.net_error = this.el.find(".network-graph .stat-error");
511
            this.net_img = this.el.find(".network-graph .stat-img");
512

    
513
            this.loading = this.el.find(".stat-busy");
514
            this.error = this.el.find(".stat-error");
515
            this.img = this.el.find(".stat-img");
516
            this.stats_period_options = this.el.find(".stats-select-option");
517
            
518

    
519
            // handle stats period option select
520
            var self = this;
521
            this.stats_period_options.click(function(){
522
                // skip if current selection is clicked
523
                if ($(this).filter(".stats-" + self.selected_stats_period).length) {
524
                    return
525
                } else {
526
                    // identify class
527
                    var cls = $(this).attr("class");
528
                    regex = /.*\sstats-(\w+)/;
529
                    self.set_stats_period(cls.match(regex)[1]);
530
                }
531
            });
532
            
533
            // initial state paremeters
534
            this.stats = this.vm.get("stats");
535

    
536
            // timeseries or bar images ?
537
            this.stats_type = options.stats_type || "bar";
538

    
539
            views.VMStatsView.__super__.initialize.apply(this, arguments);
540
            this.set_handlers();
541
            this.update_layout();
542

    
543
            this.net_loading.show();
544
            this.net_error.hide();
545
            this.cpu_loading.show();
546
            this.cpu_error.hide();
547

    
548
            this.net_img.hide();
549
            this.cpu_img.hide();
550
            
551
            if (!window.t) {
552
                window.t = [];
553
            }
554
            if (this.parent.menu) {
555
                window.t[window.t.length] = this;
556
            }
557
        },
558

    
559
        
560
        set_stats_period: function(period) {
561
            this.selected_stats_period = period;
562
            this.update_layout();
563
        },
564

    
565
        set_handlers: function() {
566
            // update view state when vm stats update gets triggered
567
            this.vm.bind("stats:update", _.bind(function(){
568
                // update the layout
569
                this.update_layout();
570
            }, this));
571
        },
572
        
573
        get_images: function (type, period) {
574
            var period = period || 'hourly';
575
            var images;
576

    
577
            if (type == 'bar') {
578
                images = {'cpu': this.stats.cpuBar, 'net': this.stats.netBar };
579
            } else {
580
                images = {'cpu': this.stats.cpuTimeSeries, 
581
                          'net': this.stats.netTimeSeries };
582
            }
583

    
584
            if (period == 'weekly' && type != 'bar') {
585
                if (images.cpu)
586
                    images.cpu = images.cpu.replace('cpu-ts', 'cpu-ts-w')
587
                if (images.net)
588
                    images.net = images.net.replace('net-ts', 'net-ts-w')
589
            }
590
            return images
591
        },
592

    
593
        update_layout: function() {
594
            if (!this.vm.stats_available) {
595
                this.loading.show();
596
                this.img.hide();
597
                this.error.hide();
598
            } else {
599
                this.loading.hide();
600
                this.stats = this.vm.get("stats");
601
                var images = this.get_images(this.stats_type, 
602
                                             this.selected_stats_period)
603

    
604
                if (images.cpu) {
605
                    this.cpu_img.attr({src:images.cpu}).show();
606
                    this.cpu_error.hide();
607
                } else {
608
                    this.cpu_img.hide();
609
                    this.cpu_error.show();
610
                }
611

    
612
                if (images.net) {
613
                    this.net_img.attr({src:images.net}).show();
614
                    this.net_error.hide();
615
                } else {
616
                    this.net_img.hide();
617
                    this.net_error.show();
618
                }
619
            }
620
                
621
            // update selected stats period
622
            this.stats_period_options.removeClass("selected");
623
            this.stats_period_options.filter(".stats-" + this.selected_stats_period).addClass("selected")
624

    
625
            $(window).trigger("resize");
626
        }
627
    });
628

    
629
    views.VMDetailsView = views.View.extend({
630
        view_id: "vm_details",
631
        el_sel: '.vm-details',
632
        
633

    
634
        selectors: {
635
            'cpu': '.cpu-data',
636
            'ram': '.ram-data',
637
            'disk': '.disk-data',
638
            'image_name': '.image-data',
639
            'image_size': '.image-size-data'
640
        },
641

    
642
        initialize: function(vm, view) {
643
            this.parent = view;
644
            this.vm = vm;
645
            this.el = $(this.parent.vm(vm)).find(this.el_sel).get(0);
646
            this.view_id = "vm_{0}_details".format(vm.id);
647
            
648
            views.VMDetailsView.__super__.initialize.call(this);
649
            
650
            this.resize_actions = this.$(".trigger-resize");
651
            this.init_handlers();
652
            this.update_layout();
653
        },
654
        
655
        init_handlers: function() {
656
          this.resize_actions.bind('click', _.bind(function(e){
657
              if (this.vm.in_error_state()) { return }
658
              ui.main.vm_resize_view.show(this.vm);
659
          }, this));
660
        },
661

    
662
        update_layout: function() {
663
            if (!this.visible() && this.parent.details_hidden) { return };
664

    
665
            var image = this.vm.get_image(_.bind(function(image){
666
                this.sel('image_name').text(
667
                  util.truncate(image.get('name'), 17)).attr("title", 
668
                  image.escape('name'));
669
                this.sel('image_size').text(
670
                  image.get_readable_size()).attr('title',
671
                                                  image.get_readable_size());
672
            }, this));
673

    
674
            var flavor = this.vm.get_flavor();
675
            if (!flavor || !image) {
676
                return;
677
            }
678

    
679

    
680
            this.sel('cpu').text(flavor.get('cpu'));
681
            this.sel('ram').text(flavor.get('ram'));
682
            this.sel('disk').text(flavor.get('disk'));
683

    
684
            this.parent.tags_views[this.vm.id].update_layout();
685
            this.parent.stats_views[this.vm.id].update_layout();
686
            
687
            if (this.parent.details_hidden) {
688
                this.vm.start_stats_update(true);
689
            }
690
        }
691
    });
692
    
693
    // VMs icon view
694
    views.IconView = views.VMListView.extend({
695
        
696
        // view id (this could be used to identify 
697
        // the view object from global context
698
        view_id: 'vm_icon',
699
        
700
        details_hidden: true,
701

    
702
        el: '#machinesview-icon',
703
        id_tpl: 'icon-vm-',
704

    
705
        selectors: {
706
            'vms': '.machine-container',
707
            'vm': '#icon-vm-',
708
            'view': '#machinesview-icon',
709
            'tpl': '#machinesview-icon.standard #machine-container-template',
710
            'spinner': '.large-spinner',
711
            'vm_spinner': '#icon-vm-{0} .state .spinner',
712
            'vm_wave': '#icon-vm-{0} .wave',
713
            'vm_cont_active': '#machinesview-icon.standard .running',
714
            'vm_cont_terminated': '#machinesview-icon.standard .terminated'
715
        },
716
            
717
        reset: function() {},
718
        // overload show function
719
        show_view: function() {
720
            $(this.el).show();
721
            this.__update_layout();
722
        },
723

    
724
        post_update_vm: function(vm) {
725
        },
726

    
727
        // identify vm model instance id based on DOM element
728
        vm_id_for_element: function(el) {
729
            return el.attr('id').replace("icon-vm-","");
730
        },
731
        
732
        // set generic view handlers
733
        set_handlers: function() {
734
        },  
735
        
736
        // stuff to do when a new vm has been created.
737
        // - create vm subviews
738
        post_add: function(vm) {
739
            // rename views index
740
            this.rename_views = this.rename_views || {};
741
            this.stats_views = this.stats_views || {};
742
            this.connect_views = this.connect_views || {};
743
            this.tags_views = this.tags_views || {};
744
            this.details_views = this.details_views || {};
745
            this.info_views = this.info_views || {};
746
            this.action_error_views = this.action_error_views || {};
747
            this.action_views = this.action_views || {};
748
            this.ports_views = this.ports_views || {};
749

    
750
            this.action_views[vm.id] = new views.VMActionsView(vm, this, this.vm(vm), this.hide_actions);
751
            this.rename_views[vm.id] = new views.IconRenameView(vm, this);
752
            this.stats_views[vm.id] = new views.VMStatsView(vm, this, {el:'.vm-stats'});
753
            this.connect_views[vm.id] = new views.IconVMConnectView(vm, this);
754
            this.tags_views[vm.id] = new views.VMTagsView(vm, this);
755
            this.details_views[vm.id] = new views.VMDetailsView(vm, this);
756
            this.action_error_views[vm.id] = new views.VMActionErrorView(vm, this);
757
            
758
            var ports_container = this.vm(vm).find(".machine-data");
759
            var ports_view = new views.VMPortListView({
760
              collection: vm.ports, 
761
              container: ports_container,
762
              parent: this
763
            });
764
            this.ports_views[vm.id] = ports_view
765
            ports_view.show();
766
            ports_view.el.hide();
767

    
768
            this.info_views[vm.id] = new views.IconInfoView(vm, this);
769
        },
770
        
771
        // vm specific event handlers
772
        set_vm_handlers: function(vm) {
773
        },
774

    
775
        check_terminated_is_empty: function() {
776
            // hide/show terminated container
777
            if (this.$(".terminated .machine-container").length == 0) {
778
                this.$(".terminated").hide()
779
            } else {
780
                this.$(".terminated").show()
781
            }
782

    
783
            $(window).trigger("resize");
784
        },
785
        
786
        // generic stuff to do on each view update
787
        // called once after each vm has been updated
788
        update_layout: function() {
789
            // TODO: why do we do this ??
790
            if (storage.vms.models.length > 0) {
791
                this.$(".running").removeClass("disabled");
792
            } else {
793
                this.$(".running").addClass("disabled");
794
            }
795
            
796
            this.check_terminated_is_empty();
797
    
798
            // FIXME: code from old js api
799
            this.$("div.separator").show();
800
            this.$("div.machine-container:last-child").find("div.separator").hide();
801
            fix_v6_addresses();
802
        },
803
  
804
        update_status_message: function(vm) {
805
            var el = this.vm(vm);
806
            var message = vm.get_status_message();
807
            if (message) {
808
                // update bulding progress
809
                el.find("div.machine-ips").hide();
810
                el.find("div.build-progress").show();
811
                el.find("div.build-progress .message").text(util.truncate(message, 42));
812

    
813
                if (vm.in_error_state()) {
814
                    el.find("div.build-progress .btn").show();
815
                } else {
816
                    el.find("div.build-progress .btn").hide();
817
                }
818
            } else {
819
                // hide building progress
820
                el.find("div.machine-ips").show()
821
                el.find("div.build-progress").hide();
822
                el.find("div.build-progress .btn").hide();
823
            }
824
        },
825

    
826
        // update vm details
827
        update_details: function(vm) {
828
            var el = this.vm(vm);
829
            // truncate name
830
            el.find("span.name").text(util.truncate(vm.get("name"), 40));
831

    
832
            el.find('.fqdn').text(vm.get('fqdn') || synnefo.config.no_fqdn_message);
833
            el.find("div.status").text(STATE_TEXTS[vm.state()]);
834
            // set state class
835
            el.find("div.state").removeClass().addClass(views.IconView.STATE_CLASSES[vm.state()].join(" "));
836
            // os icon
837
            el.find("div.logo").css({'background-image': "url(" + this.get_vm_icon_path(vm, "medium") + ")"});
838
            
839
            el.removeClass("connectable");
840
            if (vm.is_connectable()) {
841
                el.addClass("connectable");
842
            }
843
            
844
            var status = vm.get("status");
845
            var state = vm.get("state");
846
            
847
            this.update_status_message(vm);
848

    
849
            icon_state = vm.is_active() ? "on" : "off";
850
            set_machine_os_image(el, "icon", icon_state, this.get_vm_icon_os(vm));
851
            
852
            // update subviews
853
            this.rename_views[vm.id].update_layout();
854
            this.connect_views[vm.id].update_layout();
855
            this.details_views[vm.id].update_layout();
856
        },
857

    
858
        post_remove_vm: function(vm) {
859
            this.check_terminated_is_empty();
860
            $(window).trigger("resize");
861
        },
862
            
863
        get_vm_icon_os: function(vm) {
864
            var os = vm.get_os();
865
            var icons = window.os_icons || views.IconView.VM_OS_ICONS;
866

    
867
            if (icons.indexOf(os) == -1) {
868
                os = "unknown";
869
            }
870

    
871
            return os;
872
        },
873

    
874
        // TODO: move to views.utils (the method and the VM_OS_ICON vars)
875
        get_vm_icon_path: function(vm, icon_type) {
876
            var os = vm.get_os();
877
            var icons = window.os_icons || views.IconView.VM_OS_ICONS;
878

    
879
            if (icons.indexOf(os) == -1) {
880
                os = "unknown";
881
            }
882

    
883
            return views.IconView.VM_OS_ICON_TPLS()[icon_type].format(os);
884
        }
885
    })
886

    
887
    views.IconView.VM_OS_ICON_TPLS = function() {
888
        return {
889
            "medium": snf.config.machines_icons_url + "medium/{0}-sprite.png",
890
            "medium2": snf.config.machines_icons_url + "medium/{0}-sprite2.png",
891
        }
892
    }
893

    
894
    views.IconView.VM_OS_ICONS = window.os_icons || [];
895

    
896
    views.IconView.STATE_CLASSES = {
897
        'UNKNOWN':          ['state', 'error-state'],
898
        'BUILD':            ['state', 'build-state'],
899
        'REBOOT':           ['state', 'rebooting-state'],
900
        'STOPPED':          ['state', 'terminated-state'],
901
        'ACTIVE':           ['state', 'running-state'],
902
        'ERROR':            ['state', 'error-state'],
903
        'DELETED':          ['state', 'destroying-state'],
904
        'DESTROY':          ['state', 'destroying-state'],
905
        'SHUTDOWN':         ['state', 'shutting-state'],
906
        'START':            ['state', 'starting-state'],
907
        'CONNECT':          ['state', 'connecting-state'],
908
        'DISCONNECT':       ['state', 'disconnecting-state'],
909
        'RESIZE':           ['state', 'rebooting-state']
910
    };
911

    
912
})(this);