Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (31 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_link = $(".toggler", this.vm_view);
131
            this.el = $("div.info-content", this.vm_view);
132
            this.toggler = $(".toggler", this.vm_view);
133
            this.label = $(".label", this.vm_view);
134

    
135
            this.set_handlers();
136
        },
137

    
138
        set_handlers: function() {
139
            this.info_link.click(_.bind(function(){
140
                this.el.slideToggle();
141
                this.view.vm(this.vm).toggleClass("light-background");
142

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

    
157
            this.$(".stats-report").click(_.bind(function(e){
158
                e.preventDefault();
159
                snf.ui.main.show_vm_details(this.vm);
160
            }, this))
161
        }
162
    
163
    })
164

    
165
    // rename handler view
166
    // only icon view contains rename capability
167
    views.IconRenameView = views.View.extend({
168
        
169
        initialize: function(vm, view) {
170
            this.vm = vm;
171
            this.view = view;
172
            // name container
173
            this.el = $('div#' + this.view.id_tpl + vm.id + " div.name").get(0);
174
            // name inline element
175
            this.name = this.$('span.name');
176
            // rename button
177
            this.rename = this.$('span.rename');
178
            // save button
179
            this.save = this.$('.save');
180
            // cancel rename button
181
            this.cancel = this.$('.cancel');
182
            // where to place the input field
183
            this.edit_cont = this.$(".namecontainer");
184
            // buttons container
185
            this.buttons = this.$(".editbuttons");
186
            // current state
187
            this.renaming = false;
188
            // init event handlers
189
            this.set_handlers();
190
            // paint
191
            this.update_layout();
192
            views.IconRenameView.__super__.initialize.call(this);
193
        },
194
        
195
        // update elements visibility/state
196
        update_layout: function() {
197
            // if in renaming state
198
            if (this.renaming) {
199
                // if name is hidden we are already in renaming state
200
                // dont do nothing
201
                if (this.name.is(":hidden")){return}
202
                
203
                // hide name element to make space for the 
204
                // text input
205
                this.name.hide();
206
                this.rename.hide();
207
                // show confirm/cancel buttons
208
                this.buttons.show();
209
                // create text element
210
                this.create_input();
211
            } else {
212
                // name is visible not in edit mode
213
                if (this.name.is(":visible")){return}
214

    
215
                this.name.show();
216
                this.rename.show();
217
                this.buttons.hide();
218
                this.remove_input();
219
            }
220
        },
221
        
222
        // create rename input field and set appropriate 
223
        // event handlers
224
        create_input: function() {
225
            var self = this;
226
            this.edit_cont.append('<input class="vm-rename nametextbox" type="text" />');
227
            this.$('input').val(this.vm.get('name'));
228
            // give edit focus
229
            this.$('input').focus();
230
            // handle enter press
231
            this.$('input').keydown(function(ev){
232
                ev.keyCode = ev.keyCode || ev.which;
233
                if (ev.keyCode == 13) { self.submit(); }
234
                if (ev.keyCode == 27) { self.renaming = false; self.update_layout(); }
235
            })
236
        },
237
        
238
        // remove input element
239
        remove_input: function() {
240
            this.$('input').remove();
241
        },
242
        
243
        // initialize event handlers
244
        set_handlers: function() {
245
            var self = this;
246
            // start rename when rename button is pressed
247
            this.rename.click(function() {
248
                self.renaming = true;
249
                self.update_layout();
250
            });
251
            
252
            // double click on name
253
            $(this.el).dblclick(function() {
254
                self.renaming = true;
255
                self.update_layout();
256
            });
257

    
258
            // cancel rename
259
            this.cancel.click(function() {
260
                self.renaming = false;
261
                self.update_layout();
262
            })
263
            
264
            // apply the rename
265
            // TODO: check if name is equal than the previous value
266
            this.save.click(function() {
267
                self.submit();
268
            })
269
        },
270

    
271
        submit: function() {
272
            var value = _(self.$('input').val()).trim();
273
            if (value == "") { return };
274
            this.renaming = false;
275
            this.vm.rename(self.$('input').val());
276
            this.update_layout();
277
        }
278
    });
279
    
280
    // VM connect interaction view
281
    views.IconVMConnectView = views.View.extend({
282
        
283
        initialize: function(vm, view) {
284
            // parent view (single, icon, list)
285
            this.parent = view;
286
            this.vm = vm;
287
            this.el = view.vm(vm);
288
            this.set_handlers();
289
            views.IconVMConnectView.__super__.initialize.call(this);
290
        },
291
        
292
        // set the appropriate handlers
293
        set_handlers: function() {
294
            // setup connect handler on vm icon interaction
295
            var el = this.el;
296
            var vm = this.vm;
297

    
298
            // element that triggers the connect handler
299
            var connect = el.find("div.connect-arrow, .logo");
300
            // connect status handler
301
            var handler = _.bind(this.connect_handler, {vm:vm, el:el, view:this.parent});
302
            $(connect).bind({'mouseover': handler, 'mouseleave': handler, 
303
                            'mousedown': handler, 'mouseup': handler,
304
                            'click': handler });
305
            
306
            // setup connect arrow display handlers 
307
            // while hovering vm container
308
            el.bind("mouseover", function(){
309
                if (vm.is_connectable()) {
310
                    el.find(".connect-border").show();
311
                    el.find(".connect-arrow").show();
312
                    el.find(".logo").css({cursor:"pointer"});
313
                } else {
314
                    el.find(".connect-border").hide();
315
                    el.find(".connect-arrow").hide();
316
                    el.find(".logo").css({cursor: "default"});
317
                }
318
            }).bind("mouseleave", function(){
319
                el.find(".connect-border").hide();
320
                el.find(".connect-arrow").hide();
321
            });
322
        },
323
        
324
        // connect arrow interaction handlers
325
        // BEWARE, this function has different context
326
        // than the View object itself, see set_vm_handlers
327
        connect_handler: function(event) {
328
            // nothing to do if we cannot connect to the vm
329
            if (!this.vm.is_connectable()) {return}
330
            
331
            var logo = this.el.find(".logo");
332
            var arrow = this.el.find(".connect-arrow");
333
            var border = this.el.find(".connect-border");
334
            
335
            // clear icon states
336
            logo.removeClass('single-image-state1 single-image-state2 single-image-state3 single-image-state4');
337
            
338
            // append the appropriate state class
339
            switch (event.type) {
340
                case "mouseover":       
341
                    logo.addClass('single-image-state4');
342
                    arrow.addClass('border-hover');
343
                    break;
344
                
345
                case "mouseleave":
346
                    logo.addClass('single-image-state1');
347
                    arrow.removeClass('border-hover');
348
                    break;
349

    
350
                case "mouseup":
351
                    logo.addClass('single-image-state4');
352
                    //this.view.connect_overlay.show(this.vm);
353
                    break;
354

    
355
                case "mousedown":
356
                    logo.addClass('single-image-state2');
357
                    break;
358

    
359
                case "click":
360
                    //logo.addCLass('single-image-state4');
361
                    //this.view.connect_to_console(vm);
362
                    this.view.connect_overlay.show(this.vm);
363
                    break;
364

    
365
                default:
366
                    ;
367
            }
368
        },
369
        
370
        update_layout: function() {
371
        }
372

    
373
    });
374
    
375
    // vm metadata subview for icon and single view
376
    views.VMTagsView = views.View.extend({
377
        view_id: 'vm_tags',
378
        // metadata container selector
379
        el_sel: '.vm-metadata',
380
        // metadata row template
381
        tag_tpl: '<span class="tag-item"><span class="key">{0}</span><span class="value">{1}</span></span>',
382
        // how many tags to show
383
        tag_limit: 4,
384
        // truncate options (because container has different size on icon/single views)
385
        tag_key_truncate: 7,
386
        tag_value_truncate: 15,
387

    
388
        initialize: function(vm, view, toggle, limit, tag_key_truncate, tag_value_truncate) {
389
            this.tag_limit = limit || this.tag_limit;
390

    
391
            this.tag_key_truncate = tag_key_truncate || this.tag_key_truncate;
392
            this.tag_value_truncate = tag_value_truncate || this.tag_value_truncate;
393

    
394
            // does the view toggles the metadata container (single view)
395
            this.toggle = toggle || false;
396
            // parent view
397
            this.parent = view;
398
            this.vm = vm;
399
            this.el = this.parent.vm(vm);
400
            this.view_id = this.view_id + "_" + vm.id;
401

    
402
            // link to raise the metadata manager overlay
403
            this.link = this.$('a.manage-metadata');
404

    
405
            views.VMTagsView.__super__.initialize.call(this);
406
            this.set_handlers();
407
            this.update_layout();
408
        },
409
        
410
        // set the appropriate handlers
411
        set_handlers: function() {
412
            var self = this;
413
            // show the metadata editor overlay
414
            this.link.click(_.bind(function(ev) {
415
                ev.preventDefault();
416
                this.parent.metadata_view.show(this.vm);
417
            }, this));
418

    
419
            // tags have show/hide control ? bind events for them
420
            var self = this;
421
            if (this.toggle) {
422
                $(this.el).find(".tags-header").click(_.bind(function(){
423
                    $(self.el).find(".tags-content").slideToggle(600);
424
                    var toggler = $(this.el).find(".tags-header .cont-toggler");
425
                    
426
                    if (toggler.hasClass("open")) {
427
                        toggler.removeClass("open");
428
                    } else {
429
                        toggler.addClass("open");
430
                    }
431
                }, this));
432
                $(self.el).find(".tags-content").hide();
433
            }
434
        },
435
        
436
        // update metadata container and data
437
        update_layout: function() {
438

    
439
            // api metadata object
440
            var meta =  this.vm.get('metadata').values;
441

    
442
            var i = 0;
443
            var cont = $(this.el).find(".items");
444

    
445
            // clear existing items
446
            cont.find(".tag-item").remove();
447
            
448
            // create tag elements
449
            _.each(meta, function(value, key){
450
                // respect the limit
451
                if (i > this.tag_limit) {
452
                    return;
453
                }
454
                
455
                // create element
456
                var new_el = $(this.tag_tpl.format(util.truncate(key, this.tag_key_truncate), 
457
                                                 util.truncate(": " + _.escape(value), this.tag_value_truncate)));
458

    
459
                // add title attributes, improve accesibility
460
                // truncated values
461
                new_el.find("span.key").attr("title", key);
462
                new_el.find("span.value").attr("title", _.escape(value));
463

    
464
                cont.append(new_el);
465
            }, this);
466
        }
467
    });
468
    
469

    
470
    // stats subview for single/icon views
471
    views.VMStatsView = views.View.extend({
472

    
473
        initialize: function(vm, view, options) {
474
            if (!options) {options = {}};
475
            this.vm = vm;
476
            this.parent = view;
477
            this.sel = options.el || this.el_sel || ".lower";
478
            this.el = this.parent.vm(vm).find(this.sel);
479
            this.selected_stats_period = 'hourly';
480
            
481
            // elements shortcuts
482
            this.cpu_loading = this.el.find(".cpu-graph .stat-busy");
483
            this.cpu_error = this.el.find(".cpu-graph .stat-error");
484
            this.cpu_img = this.el.find(".cpu-graph .stat-img");
485
            this.net_loading = this.el.find(".network-graph .stat-busy");
486
            this.net_error = this.el.find(".network-graph .stat-error");
487
            this.net_img = this.el.find(".network-graph .stat-img");
488

    
489
            this.loading = this.el.find(".stat-busy");
490
            this.error = this.el.find(".stat-error");
491
            this.img = this.el.find(".stat-img");
492
            this.stats_period_options = this.el.find(".stats-select-option");
493
            
494

    
495
            // handle stats period option select
496
            var self = this;
497
            this.stats_period_options.click(function(){
498
                // skip if current selection is clicked
499
                if ($(this).filter(".stats-" + self.selected_stats_period).length) {
500
                    return
501
                } else {
502
                    // identify class
503
                    var cls = $(this).attr("class");
504
                    regex = /.*\sstats-(\w+)/;
505
                    self.set_stats_period(cls.match(regex)[1]);
506
                }
507
            });
508
            
509
            // initial state paremeters
510
            this.stats = this.vm.get("stats");
511

    
512
            // timeseries or bar images ?
513
            this.stats_type = options.stats_type || "bar";
514

    
515
            views.VMStatsView.__super__.initialize.apply(this, arguments);
516
            this.set_handlers();
517
            this.update_layout();
518

    
519
            this.net_loading.show();
520
            this.net_error.hide();
521
            this.cpu_loading.show();
522
            this.cpu_error.hide();
523

    
524
            this.net_img.hide();
525
            this.cpu_img.hide();
526
            
527
            if (!window.t) {
528
                window.t = [];
529
            }
530
            if (this.parent.menu) {
531
                window.t[window.t.length] = this;
532
            }
533
        },
534

    
535
        
536
        set_stats_period: function(period) {
537
            this.selected_stats_period = period;
538
            this.update_layout();
539
        },
540

    
541
        set_handlers: function() {
542
            // update view state when vm stats update gets triggered
543
            this.vm.bind("stats:update", _.bind(function(){
544
                // update the layout
545
                this.update_layout();
546
            }, this));
547
        },
548
        
549
        get_images: function (type, period) {
550
            var period = period || 'hourly';
551
            var images;
552

    
553
            if (type == 'bar') {
554
                images = {'cpu': this.stats.cpuBar, 'net': this.stats.netBar };
555
            } else {
556
                images = {'cpu': this.stats.cpuTimeSeries, 
557
                          'net': this.stats.netTimeSeries };
558
            }
559

    
560
            if (period == 'weekly' && type != 'bar') {
561
                if (images.cpu)
562
                    images.cpu = images.cpu.replace('cpu-ts', 'cpu-ts-w')
563
                if (images.net)
564
                    images.net = images.net.replace('net-ts', 'net-ts-w')
565
            }
566
            return images
567
        },
568

    
569
        update_layout: function() {
570
            if (!this.vm.stats_available) {
571
                this.loading.show();
572
                this.img.hide();
573
                this.error.hide();
574
            } else {
575
                this.loading.hide();
576
                this.stats = this.vm.get("stats");
577
                var images = this.get_images(this.stats_type, 
578
                                             this.selected_stats_period)
579

    
580
                if (images.cpu) {
581
                    this.cpu_img.attr({src:images.cpu}).show();
582
                    this.cpu_error.hide();
583
                } else {
584
                    this.cpu_img.hide();
585
                    this.cpu_error.show();
586
                }
587

    
588
                if (images.net) {
589
                    this.net_img.attr({src:images.net}).show();
590
                    this.net_error.hide();
591
                } else {
592
                    this.net_img.hide();
593
                    this.net_error.show();
594
                }
595
            }
596
                
597
            // update selected stats period
598
            this.stats_period_options.removeClass("selected");
599
            this.stats_period_options.filter(".stats-" + this.selected_stats_period).addClass("selected")
600

    
601
            $(window).trigger("resize");
602
        }
603
    });
604

    
605
    views.VMDetailsView = views.View.extend({
606
        view_id: "vm_details",
607
        el_sel: '.vm-details',
608
        
609

    
610
        selectors: {
611
            'cpu': '.cpu-data',
612
            'ram': '.ram-data',
613
            'disk': '.disk-data',
614
            'image_name': '.image-data',
615
            'image_size': '.image-size-data'
616
        },
617

    
618
        initialize: function(vm, view) {
619
            this.parent = view;
620
            this.vm = vm;
621
            this.el = $(this.parent.vm(vm)).find(this.el_sel).get(0);
622
            this.view_id = "vm_{0}_details".format(vm.id);
623
            
624
            views.VMDetailsView.__super__.initialize.call(this);
625

    
626
            this.update_layout();
627
        },
628

    
629
        update_layout: function() {
630
            if (!this.visible() && this.parent.details_hidden) { return };
631

    
632
            var image = this.vm.get_image(_.bind(function(image){
633
                this.sel('image_name').text(util.truncate(image.escape('name'), 17)).attr("title", image.escape('name'));
634
                this.sel('image_size').text(image.get_readable_size()).attr('title', image.get_readable_size());
635
            }, this));
636

    
637
            var flavor = this.vm.get_flavor();
638
            if (!flavor || !image) {
639
                return;
640
            }
641

    
642

    
643
            this.sel('cpu').text(flavor.get('cpu'));
644
            this.sel('ram').text(flavor.get('ram'));
645
            this.sel('disk').text(flavor.get('disk'));
646

    
647
            this.parent.tags_views[this.vm.id].update_layout();
648
            this.parent.stats_views[this.vm.id].update_layout();
649
            
650
            if (this.parent.details_hidden) {
651
                this.vm.start_stats_update(true);
652
            }
653
        }
654
    });
655
    
656
    // VMs icon view
657
    views.IconView = views.VMListView.extend({
658
        
659
        // view id (this could be used to identify 
660
        // the view object from global context
661
        view_id: 'vm_icon',
662
        
663
        details_hidden: true,
664

    
665
        el: '#machinesview-icon',
666
        id_tpl: 'icon-vm-',
667

    
668
        selectors: {
669
            'vms': '.machine-container',
670
            'vm': '#icon-vm-',
671
            'view': '#machinesview-icon',
672
            'tpl': '#machinesview-icon.standard #machine-container-template',
673
            'spinner': '.large-spinner',
674
            'vm_spinner': '#icon-vm-{0} .state .spinner',
675
            'vm_wave': '#icon-vm-{0} .wave',
676
            'vm_cont_active': '#machinesview-icon.standard .running',
677
            'vm_cont_terminated': '#machinesview-icon.standard .terminated'
678
        },
679
            
680
        reset: function() {},
681
        // overload show function
682
        show_view: function() {
683
            $(this.el).show();
684
            this.__update_layout();
685
        },
686

    
687
        post_update_vm: function(vm) {
688
        },
689

    
690
        // identify vm model instance id based on DOM element
691
        vm_id_for_element: function(el) {
692
            return el.attr('id').replace("icon-vm-","");
693
        },
694
        
695
        // set generic view handlers
696
        set_handlers: function() {
697
        },  
698
        
699
        // stuff to do when a new vm has been created.
700
        // - create vm subviews
701
        post_add: function(vm) {
702
            // rename views index
703
            this.rename_views = this.rename_views || {};
704
            this.stats_views = this.stats_views || {};
705
            this.connect_views = this.connect_views || {};
706
            this.tags_views = this.tags_views || {};
707
            this.details_views = this.details_views || {};
708
            this.info_views = this.info_views || {};
709
            this.action_error_views = this.action_error_views || {};
710
            this.action_views = this.action_views || {};
711

    
712
            this.action_views[vm.id] = new views.VMActionsView(vm, this, this.vm(vm), this.hide_actions);
713
            this.rename_views[vm.id] = new views.IconRenameView(vm, this);
714
            this.stats_views[vm.id] = new views.VMStatsView(vm, this, {el:'.vm-stats'});
715
            this.connect_views[vm.id] = new views.IconVMConnectView(vm, this);
716
            this.tags_views[vm.id] = new views.VMTagsView(vm, this);
717
            this.details_views[vm.id] = new views.VMDetailsView(vm, this);
718
            this.info_views[vm.id] = new views.IconInfoView(vm, this);
719
            this.action_error_views[vm.id] = new views.VMActionErrorView(vm, this);
720
        },
721
        
722
        // vm specific event handlers
723
        set_vm_handlers: function(vm) {
724
        },
725

    
726
        check_terminated_is_empty: function() {
727
            // hide/show terminated container
728
            if (this.$(".terminated .machine-container").length == 0) {
729
                this.$(".terminated").hide()
730
            } else {
731
                this.$(".terminated").show()
732
            }
733

    
734
            $(window).trigger("resize");
735
        },
736
        
737
        // generic stuff to do on each view update
738
        // called once after each vm has been updated
739
        update_layout: function() {
740
            // TODO: why do we do this ??
741
            if (storage.vms.models.length > 0) {
742
                this.$(".running").removeClass("disabled");
743
            } else {
744
                this.$(".running").addClass("disabled");
745
            }
746
            
747
            this.check_terminated_is_empty();
748
    
749
            // FIXME: code from old js api
750
            this.$("div.separator").show();
751
            this.$("div.machine-container:last-child").find("div.separator").hide();
752
            fix_v6_addresses();
753
        },
754
  
755
        update_status_message: function(vm) {
756
            var el = this.vm(vm);
757
            var message = vm.get_status_message();
758
            if (message) {
759
                // update bulding progress
760
                el.find("div.machine-ips").hide();
761
                el.find("div.build-progress").show();
762
                el.find("div.build-progress .message").text(util.truncate(message, 42));
763

    
764
                if (vm.in_error_state()) {
765
                    el.find("div.build-progress .btn").show();
766
                } else {
767
                    el.find("div.build-progress .btn").hide();
768
                }
769
            } else {
770
                // hide building progress
771
                el.find("div.machine-ips").show()
772
                el.find("div.build-progress").hide();
773
                el.find("div.build-progress .btn").hide();
774
            }
775
        },
776

    
777
        // update vm details
778
        update_details: function(vm) {
779
            var el = this.vm(vm);
780
            // truncate name
781
            el.find("span.name").text(util.truncate(vm.get("name"), 40));
782
            // set ips
783
            el.find("span.ipv4-text").text(vm.get_addresses().ip4 || "not set");
784
            // TODO: fix ipv6 truncates and tooltip handler
785
            el.find("span.ipv6-text").text(vm.get_addresses().ip6 || "not set");
786
            // set the state (i18n ??)
787
            el.find("div.status").text(STATE_TEXTS[vm.state()]);
788
            // set state class
789
            el.find("div.state").removeClass().addClass(views.IconView.STATE_CLASSES[vm.state()].join(" "));
790
            // os icon
791
            el.find("div.logo").css({'background-image': "url(" + this.get_vm_icon_path(vm, "medium") + ")"});
792
            
793
            el.removeClass("connectable");
794
            if (vm.is_connectable()) {
795
                el.addClass("connectable");
796
            }
797
            
798
            var status = vm.get("status");
799
            var state = vm.get("state");
800
            
801
            this.update_status_message(vm);
802

    
803
            icon_state = vm.is_active() ? "on" : "off";
804
            set_machine_os_image(el, "icon", icon_state, this.get_vm_icon_os(vm));
805
            
806
            // update subviews
807
            this.rename_views[vm.id].update_layout();
808
            this.connect_views[vm.id].update_layout();
809
            this.details_views[vm.id].update_layout();
810
        },
811

    
812
        post_remove_vm: function(vm) {
813
            this.check_terminated_is_empty();
814
            $(window).trigger("resize");
815
        },
816
            
817
        get_vm_icon_os: function(vm) {
818
            var os = vm.get_os();
819
            var icons = window.os_icons || views.IconView.VM_OS_ICONS;
820

    
821
            if (icons.indexOf(os) == -1) {
822
                os = "unknown";
823
            }
824

    
825
            return os;
826
        },
827

    
828
        // TODO: move to views.utils (the method and the VM_OS_ICON vars)
829
        get_vm_icon_path: function(vm, icon_type) {
830
            var os = vm.get_os();
831
            var icons = window.os_icons || views.IconView.VM_OS_ICONS;
832

    
833
            if (icons.indexOf(os) == -1) {
834
                os = "unknown";
835
            }
836

    
837
            return views.IconView.VM_OS_ICON_TPLS()[icon_type].format(os);
838
        }
839
    })
840

    
841
    views.IconView.VM_OS_ICON_TPLS = function() {
842
        return {
843
            "medium": snf.config.machines_icons_url + "medium/{0}-sprite.png"
844
        }
845
    }
846

    
847
    views.IconView.VM_OS_ICONS = window.os_icons || [];
848

    
849
    views.IconView.STATE_CLASSES = {
850
        'UNKNOWN':          ['state', 'error-state'],
851
        'BUILD':            ['state', 'build-state'],
852
        'REBOOT':           ['state', 'rebooting-state'],
853
        'STOPPED':          ['state', 'terminated-state'],
854
        'ACTIVE':           ['state', 'running-state'],
855
        'ERROR':            ['state', 'error-state'],
856
        'DELETED':           ['state', 'destroying-state'],
857
        'DESTROY':          ['state', 'destroying-state'],
858
        'BUILD_INIT':       ['state', 'build-state'], 
859
        'BUILD_COPY':       ['state', 'build-state'],
860
        'BUILD_FINAL':      ['state', 'build-state'],
861
        'SHUTDOWN':         ['state', 'shutting-state'],
862
        'START':            ['state', 'starting-state'],
863
        'CONNECT':          ['state', 'connecting-state'],
864
        'DISCONNECT':       ['state', 'disconnecting-state']
865
    };
866

    
867
})(this);