Statistics
| Branch: | Tag: | Revision:

root / ui / static / snf / js / ui / web / ui_main_view.js @ 1e827d67

History | View | Annotate | Download (29.1 kB)

1
;(function(root){
2

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

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

    
14
    // shortcuts
15
    var bb = root.Backbone;
16
    var util = snf.util;
17
    
18
    views.ApiInfoView = views.Overlay.extend({
19
        view_id: "api_info_view",
20
        
21
        content_selector: "#api-info-overlay",
22
        css_class: 'overlay-api-info overlay-info',
23
        overlay_id: "overlay-api-info",
24

    
25
        subtitle: "",
26
        title: "API Access",
27

    
28
        beforeOpen: function() {
29
            var cont = this.$(".copy-content p");
30
            var token = $.cookie("X-Auth-Token");
31

    
32
            cont.html("");
33
            cont.text(token);
34

    
35
            var clip = new snf.util.ClipHelper();
36
            cont.parent().append(clip.cont);
37
            clip.setText(token);
38
        },
39

    
40
        onClose: function() {
41
            var cont = this.$(".copy-content p");
42
            var token = $.cookie("X-Auth-Token");
43
            cont.html("");
44
        }
45
    });
46

    
47
    // TODO: implement me
48
    views.NoticeView = views.Overlay.extend({});
49

    
50
    views.MultipleActionsView = views.View.extend({
51
        view_id: "multiple_actions",
52

    
53
        _actions: {},
54
        el: '#multiple_actions_container',
55
        
56
        initialize: function() {
57
            this.actions = {};
58
            views.MultipleActionsView.__super__.initialize.call(this);
59
            
60
            // view elements
61
            this.confirm_actions = this.$(".confirm_multiple_actions");
62
            this.confirm_actions_yes = this.$(".confirm_multiple_actions button.yes");
63
            this.confirm_actions_no = this.$(".confirm_multiple_actions button.no");
64
            this.confirm_reboot = this.$(".confirm_reboot_required");
65
            this.confirm_reboot_yes = this.$(".confirm_reboot_required button.yes");
66
            this.confirm_reboot_no = this.$(".confirm_reboot_required button.no");
67
            this.confirm_reboot_list = this.confirm_reboot.find(".reboot-machines-list");
68

    
69
            this.init_handlers();
70
            this.update_layout();
71
            
72
            // for heavy resize/scroll window events
73
            // do it `like a boss` 
74
            this.fix_position = _.throttle(this.fix_position, 100);
75
            this.update_layout = _.throttle(this.update_layout, 100);
76
            this.show_limit = 1;
77
        },
78

    
79
        init_handlers: function() {
80
            var self = this;
81

    
82
            $(window).resize(_.bind(function(){
83
                this.fix_position();
84
            }, this));
85

    
86
            $(window).scroll(_.bind(function(){
87
                this.fix_position();
88
            }, this));
89
            
90
            // confirm/cancel button handlers
91
            var self = this;
92
            this.confirm_actions_yes.click(function(){ self.do_all(); })
93
            this.confirm_actions_no.click(function(){
94
                self.reset_actions();
95
            });
96

    
97
            this.confirm_reboot_yes.click(function(){ self.do_reboot_all(); })
98
            this.confirm_reboot_no.click(function(){
99
                self.reset_reboots();
100
            });
101

    
102
            storage.vms.bind("change:pending_action", _.bind(this.handle_vm_change, this));
103
            storage.vms.bind("change:reboot_required", _.bind(this.handle_vm_change, this));
104

    
105
        },
106

    
107
        handle_vm_change: function(vm) {
108
            if (vm.has_pending_action()) {
109
                var action = vm.get("pending_action");
110
                this.add_action(vm, action);
111
            } else {
112
                this.remove_action(vm);
113
            }
114
            this.update_layout();
115
        },
116

    
117
        add_action: function(vm, action) {
118
            this._actions[vm.id] = {'vm': vm, 'action': action};
119
        },
120

    
121
        remove_action: function(vm) {
122
            delete this._actions[vm.id];
123
        },
124

    
125
        reset: function() {
126
            this._actions = {};
127
            this.update_layout();
128
        },
129
        
130
        reboot_vm: function(vm) {
131
            vm.call("reboot");
132
        },
133

    
134
        do_reboot_all: function() {
135
            _.each(storage.vms.get_reboot_required(), function(vm){
136
                this.reboot_vm(vm)
137
            }, this)  
138
        },
139

    
140
        do_all: function() {
141
            _.each(this._actions, function(action){
142
                action.vm.call(action.action);
143
            }, this)  
144
            this.reset_actions();
145
        },
146

    
147
        reset_reboots: function () {
148
            _.each(storage.vms.get_reboot_required(), function(vm) {vm.set({'reboot_required': false})}, this);
149
            this.update_layout();
150
        },
151

    
152
        reset_actions: function() {
153
            _.each(this._actions, _.bind(function(action){
154
                try {
155
                    action.vm.clear_pending_action();
156
                    this.remove_action(action.vm);
157
                } catch(err) {
158
                    console.error("vm " + action.vm.id + " failed to reset", err);
159
                }
160
            }, this))  
161
        },
162
        
163
        fix_position: function() {
164
            $('.confirm_multiple').removeClass('fixed');
165
            if (($(this.el).offset().top +$(this.el).height())> ($(window).scrollTop() + $(window).height())) {
166
                $('.confirm_multiple').addClass('fixed');
167
            }
168
        },
169
        
170
        check_notify_limit: function() {
171
            this.show_limit = 1;
172
            if (ui.main.current_view && ['networks', 'vm_list'].indexOf(ui.main.current_view.view_id) > -1) {
173
                this.show_limit = 0;
174
            }
175
        },
176
        
177
        update_reboot_required_list: function(vms) {
178
            this.confirm_reboot_list.empty();
179
        },
180

    
181
        update_reboot_required: function() {
182
            var vms = storage.vms.get_reboot_required();
183
            if (vms.length) {
184
                this.confirm_reboot.find(".actionLen").text(vms.length);
185
                this.update_reboot_required_list();
186
                this.confirm_reboot.show();
187
                $(this.el).show();
188
            } else {
189
                if (!this.actions_visible) {
190
                   $(this.el).hide();
191
                }
192
                this.confirm_reboot.hide();
193
            }
194
        },
195

    
196
        update_layout: function() {
197
            this.check_notify_limit();
198
            this.actions_visible = false;
199

    
200
            if (_.size(this._actions) > this.show_limit) {
201
                this.actions_visible = true;
202
                $(this.el).show();
203
                this.confirm_actions.show();
204
            } else {
205
                $(this.el).hide();
206
                this.confirm_actions.hide();
207
            }
208

    
209
            this.update_reboot_required();
210
            this.confirm_actions.find(".actionLen").text(_.size(this._actions));
211
            $(window).trigger("resize");
212
        }
213
    })
214
    
215
    // menu wrapper view
216
    views.SelectView = views.View.extend({
217
        
218
        initialize: function(view) {
219
            this.parent = view;
220

    
221
            this.pane_view_selector = $(".css-tabs");
222
            this.machine_view_selector = $("#view-select");
223
            this.el = $(".css-tabs");
224
            this.title = $(".tab-name");
225

    
226
            this.set_handlers();
227
            this.update_layout();
228

    
229
            views.SelectView.__super__.initialize.apply(this, arguments);
230
        },
231
        
232
        clear_active: function() {
233
            this.pane_view_selector.find("a").removeClass("active");
234
            this.machine_view_selector.find("a").removeClass("activelink");
235
        },
236
        
237
        // intercept menu links
238
        set_handlers: function() {
239
            var self = this;
240
            this.pane_view_selector.find("a").hover(function(){
241
                // FIXME: title from href ? omg
242
                self.title.text($(this).attr("href"));
243
            }, function(){
244
                self.title.text(self.parent.get_title());
245
            });
246

    
247
            this.pane_view_selector.find("a#machines_view_link").click(_.bind(function(ev){
248
                ev.preventDefault();
249
                this.parent.show_view("machines");
250
            }, this))
251
            this.pane_view_selector.find("a#networks_view_link").click(_.bind(function(ev){
252
                ev.preventDefault();
253
                this.parent.show_view("networks");
254
            }, this))
255
            this.pane_view_selector.find("a#disks_view_link").click(_.bind(function(ev){
256
                ev.preventDefault();
257
                this.parent.show_view("disks");
258
            }, this))
259
            
260
            this.machine_view_selector.find("a#machines_view_icon_link").click(_.bind(function(ev){
261
                ev.preventDefault();
262
                var d = $.now();
263
                this.parent.show_view("icon");
264
            }, this))
265
            this.machine_view_selector.find("a#machines_view_list_link").click(_.bind(function(ev){
266
                ev.preventDefault();
267
                this.parent.show_view("list");
268
            }, this))
269
            this.machine_view_selector.find("a#machines_view_single_link").click(_.bind(function(ev){
270
                ev.preventDefault();
271
                this.parent.show_view("single");
272
            }, this))
273
        },
274

    
275
        update_layout: function() {
276
            this.clear_active();
277

    
278
            var pane_index = this.parent.pane_ids[this.parent.current_view_id];
279
            $(this.pane_view_selector.find("a")).removeClass("active");
280
            $(this.pane_view_selector.find("a").get(pane_index)).addClass("active");
281
            
282
            if (this.parent.current_view && this.parent.current_view.vms_view) {
283

    
284
                if (storage.vms.length > 0) {
285
                    this.machine_view_selector.show();
286
                    var machine_index = this.parent.views_ids[this.parent.current_view_id];
287
                    $(this.machine_view_selector.find("a").get(machine_index)).addClass("activelink");
288
                } else {
289
                    this.machine_view_selector.hide();
290
                }
291
            } else {
292
                this.machine_view_selector.hide();
293
            }
294

    
295
        }
296
    });
297

    
298
    views.MainView = views.View.extend({
299
        el: 'body',
300
        view_id: 'main',
301
        
302
        // FIXME: titles belong to SelectView
303
        views_titles: {
304
            'icon': 'machines', 'single': 'machines', 
305
            'list': 'machines', 'networks': 'networks',
306
            'disks': 'disks'
307
        },
308

    
309
        // indexes registry
310
        views_indexes: {0: 'icon', 2:'single', 1: 'list', 3:'networks'},
311
        views_pane_indexes: {0:'single', 1:'networks', 2:'disks'},
312

    
313
        // views classes registry
314
        views_classes: {'icon': views.IconView, 'single': views.SingleView, 
315
            'list': views.ListView, 'networks': views.NetworksView},
316

    
317
        // view ids
318
        views_ids: {'icon':0, 'single':2, 'list':1, 'networks':3},
319

    
320
        // on which pane id each view exists
321
        // machine views (icon,single,list) are all on first pane
322
        pane_ids: {'icon':0, 'single':0, 'list':0, 'networks':1, 'disks':2},
323
    
324
        initialize: function(show_view) {
325
            if (!show_view) { show_view = 'icon' };
326
                
327
            this.empty_hidden = true;
328
            // fallback to browser error reporting (true for debug)
329
            this.skip_errors = true
330

    
331
            // reset views
332
            this.views = {};
333

    
334
            this.el = $("#app");
335
            // reset main view status
336
            this._loaded = false;
337
            this.status = "Initializing...";
338

    
339
            // initialize handlers
340
            this.init_handlers();
341

    
342
            // identify initial view from user cookies
343
            // this view will be visible after loading of
344
            // main view
345
            this.initial_view = this.session_view();
346

    
347
            views.MainView.__super__.initialize.call(this);
348

    
349
            $(window).focus(_.bind(this.handle_window_focus, this, "focus"));
350
            $(window).blur(_.bind(this.handle_window_focus, this, "out"));
351

    
352
            this.focused = true;
353
        },
354

    
355
        handle_window_focus: function(focus) {
356
            if (!snf.config.delay_on_blur) { return };
357

    
358
            if (focus === "focus") {
359
                this.focused = true;
360
                this.set_interval_timeouts(snf.config.update_interval);
361
            } else {
362
                this.focused = false;
363
                this.set_interval_timeouts(snf.config.blur_delay);
364
            }
365
        },
366

    
367
        set_interval_timeouts: function(time) {
368
            _.each([this._networks, this._vms], function(fetcher){
369
                if (!fetcher) { return };
370
                fetcher.timeout = time;
371
                fetcher.stop().start();
372
            })
373
        },
374
        
375
        vms_handlers_registered: false,
376

    
377
        // register event handlers
378
        // 
379
        // vms storage events to identify if vms list 
380
        // is empty and display empty view if user viewing
381
        // a machine view
382
        //
383
        // api/ui error event handlers
384
        init_handlers: function() {
385
            // vm handlers
386
            storage.vms.bind("remove", _.bind(this.check_empty, this));
387
            storage.vms.bind("add", _.bind(this.check_empty, this));
388
            storage.vms.bind("change:status", _.bind(this.check_empty, this));
389
            storage.vms.bind("reset", _.bind(this.check_empty, this));
390
            
391
            // api calls handlers
392
            synnefo.api.bind("error", _.bind(this.handle_api_error, this));
393
            synnefo.api.bind("change:error_state", _.bind(this.handle_api_error_state, this));
394
            synnefo.ui.bind("error", _.bind(this.handle_ui_error, this));
395
        },
396
        
397
        handle_api_error_state: function(state) {
398
            if (snf.api.error_state === snf.api.STATES.ERROR) {
399
                this.stop_intervals();
400
            } else {
401
                if (this.intervals_stopped) {
402
                    this.update_intervals();
403
                }
404
            }
405
        },
406
        
407
        handle_api_error: function(args) {
408
            if (arguments.length == 1) { arguments = _.toArray(arguments[0])};
409

    
410
            if (!_.last(arguments).display) {
411
                return;
412
            }
413

    
414
            this.error_state = true;
415
            
416
            var xhr = arguments[0];
417
            var args = util.parse_api_error.apply(util, arguments);
418
            
419
            // force logout if UNAUTHORIZED request arrives
420
            if (args.code == 401) { snf.ui.logout(); return };
421

    
422
            var error_entry = [args.ns, args.code, args.message, args.type, args.details, args];
423
            this.error_view.show_error.apply(this.error_view, error_entry);
424
        },
425

    
426
        handle_ui_error: function(data) {
427
            var msg = data.msg, code = data.code, err_obj = data.error;
428
            error = msg + "<br /><br />" + snf.util.stacktrace().replace("at", "<br /><br />at");
429
            params = { title: "UI error", extra_details: data.extra };
430
            params.allow_close = data.extra.allow_close === undefined ? true : data.extra.allow_close;
431
            this.error_view.show_error("UI", -1, msg, "JS Exception", error, params);
432
        },
433

    
434
        init_overlays: function() {
435
            this.create_vm_view = new views.CreateVMView();
436
            this.api_info_view = new views.ApiInfoView();
437
            //this.notice_view = new views.NoticeView();
438
        },
439
        
440
        show_loading_view: function() {
441
            $("#container #content").hide();
442
            $("#loading-view").show();
443
        },
444

    
445
        hide_loading_view: function() {
446
            $("#container #content").show();
447
            $("#loading-view").hide();
448
            $(".css-panes").show();
449
        },
450
        
451
        items_to_load: 4,
452
        completed_items: 0,
453
        check_status: function(loaded) {
454
            this.completed_items++;
455
            // images, flavors loaded
456
            if (this.completed_items == 2) {
457
                this.load_nets_and_vms();
458
            }
459

    
460
            if (this.completed_items == this.items_to_load) {
461
                this.update_status("Rendering layout...");
462
                var self = this;
463
                window.setTimeout(function(){
464
                    self.after_load();
465
                }, 10)
466
            }
467
        },
468

    
469
        load_nets_and_vms: function() {
470
            var self = this;
471
            this.update_status("Loading vms...");
472
            storage.vms.fetch({refresh:true, update:false, success: function(){
473
                self.update_status("VMS Loaded.");
474
                self.check_status();
475
            }});
476

    
477
            this.update_status("Loading networks...");
478
            storage.networks.fetch({refresh:true, update:false, success: function(){
479
                self.update_status("Networks loaded.");
480
                self.check_status();
481
            }});
482
        },  
483

    
484
        init_intervals: function() {
485
            this._networks = storage.networks.get_fetcher(snf.config.update_interval, 
486
                                                          snf.config.update_interval / 2, 
487
                                                          1, true, undefined);
488
            this._vms = storage.vms.get_fetcher(snf.config.update_interval, 
489
                                                snf.config.update_interval / 2, 
490
                                                1, true, undefined);
491
        },
492

    
493
        stop_intervals: function() {
494
            if (this._networks) { this._networks.stop(); }
495
            if (this._vms) { this._vms.stop(); }
496
            this.intervals_stopped = true;
497
        },
498

    
499
        update_intervals: function() {
500
            if (this._networks) {
501
                this._networks.stop();
502
                this._networks.start();
503
            } else {
504
                this.init_intervals();
505
            }
506

    
507
            if (this._vms) {
508
                this._vms.stop();
509
                this._vms.start();
510
            } else {
511
                this.init_intervals();
512
            }
513

    
514
            this.intervals_stopped = false;
515
        },
516

    
517
        after_load: function() {
518
            var self = this;
519
            this.update_status("Setting vms update interval...");
520
            this.init_intervals();
521
            this.update_intervals();
522
            this.update_status("Showing initial view...");
523
            
524
            // bypass update_hidden_views in initial view
525
            // rendering to force all views to get render
526
            // on their creation
527
            var uhv = snf.config.update_hidden_views;
528
            snf.config.update_hidden_views = true;
529
            this.initialize_views()
530
            snf.config.update_hidden_views = uhv;
531

    
532
            window.setTimeout(function() {
533
                self.update_status("Initializing overlays...");
534
                self.load_initialize_overlays();
535
            }, 20);
536
        },
537

    
538
        load_initialize_overlays: function() {
539
            this.init_overlays();
540
            // display initial view
541
            this.loaded = true;
542
            this.show_initial_view();
543
            this.check_empty();
544
        },
545

    
546
        load: function() {
547
            this.error_view = new views.ErrorView();
548
            this.feedback_view = new views.FeedbackView();
549
            this.invitations_view = new views.InvitationsView();
550
            var self = this;
551
            // initialize overlay views
552
            
553
            // display loading message
554
            this.show_loading_view();
555
            // sync load initial data
556
            this.update_status("Loading images...");
557
            storage.images.fetch({refresh:true, update:false, success: function(){
558
                self.check_status()
559
            }});
560
            this.update_status("Loading flavors...");
561
            storage.flavors.fetch({refresh:true, update:false, success:function(){
562
                self.check_status()
563
            }});
564
        },
565

    
566
        update_status: function(msg) {
567
            this.log.debug(msg)
568
            this.status = msg;
569
            $("#loading-view .info").removeClass("hidden")
570
            $("#loading-view .info").text(this.status);
571
        },
572

    
573
        initialize_views: function() {
574
            this.empty_view = new views.EmptyView();
575
            this.select_view = new views.SelectView(this);
576
            this.metadata_view = new views.MetadataView();
577
            this.multiple_actions_view = new views.MultipleActionsView();
578
            
579
            this.add_view("icon");
580
            this.add_view("list");
581
            this.add_view("single");
582
            this.add_view("networks");
583

    
584
            this.init_menu();
585
        },
586

    
587
        init_menu: function() {
588
            $(".usermenu .invitations").click(_.bind(function(){
589
                this.invitations_view.show();
590
            }, this));
591
            $(".usermenu .feedback").click(_.bind(function(){
592
                this.feedback_view.show();
593
            }, this));
594
        },
595
        
596
        // initial view based on user cookie
597
        show_initial_view: function() {
598
          this.set_vm_view_handlers();
599
          this.hide_loading_view();
600
          this.show_view(this.initial_view);
601
          this.trigger("initial");
602
        },
603

    
604
        show_vm_details: function(vm) {
605
            snf.ui.main.show_view("single")
606
            snf.ui.main.current_view.show_vm(vm);
607
        },
608

    
609
        set_vm_view_handlers: function() {
610
            $("#createcontainer #create").click(_.bind(function(){
611
                this.create_vm_view.show();
612
            }, this))
613
        },
614

    
615
        check_empty: function() {
616
            if (!this.loaded) { return }
617
            if (storage.vms.length == 0) {
618
                this.show_view("machines");
619
                this.show_empty();
620
                this.empty_hidden = false;
621
            } else {
622
                this.hide_empty();
623
            }
624
        },
625

    
626
        show_empty: function() {
627
            if (!this.empty_hidden) { return };
628
            $("#machines-pane-top").addClass("empty");
629

    
630
            this.$(".panes").hide();
631
            this.$("#machines-pane").show();
632

    
633
            this.hide_views([]);
634
            this.empty_hidden = false;
635
            this.empty_view.show();
636
            this.select_view.update_layout();
637
            this.empty_hidden = false;
638
        },
639

    
640
        hide_empty: function() {
641
            if (this.empty_hidden) { return };
642
            $("#machines-pane-top").removeClass("empty");
643

    
644
            this.empty_view.hide(true);
645
            if (this.current_view && !this.current_view.visible()) { 
646
                this.current_view.show(); 
647
            }
648
            this.empty_hidden = true;
649
            this.select_view.update_layout();
650
        },
651
        
652
        get_title: function(view_id) {
653
            var view_id = view_id || this.current_view_id;
654
            return this.views_titles[view_id];
655
        },
656

    
657
        // return class object for the given view or false if
658
        // the view is not registered
659
        get_class_for_view: function (view_id) {
660
            if (!this.views_classes[view_id]) {
661
                return false;
662
            }
663
            return this.views_classes[view_id];
664
        },
665

    
666
        view: function(view_id) {
667
            return this.views[view_id];
668
        },
669

    
670
        add_view: function(view_id) {
671
            if (!this.views[view_id]) {
672
                var cls = this.get_class_for_view(view_id);
673
                if (this.skip_errors) {
674
                    this.views[view_id] = new cls();
675
                    $(this.views[view_id]).bind("resize", _.bind(function() {
676
                        window.positionFooter();
677
                        this.multiple_actions_view.fix_position();
678
                    }, this));
679
                } else {
680
                    // catch ui errors
681
                    try {
682
                        this.views[view_id] = new cls();
683
                        $(this.views[view_id]).bind("resize", _.bind(function() {
684
                            window.positionFooter();
685
                            this.multiple_actions_view.fix_position();
686
                        }, this));
687
                    } catch (err) {snf.ui.trigger_error(-1, "Cannot add view", err)}
688
                }
689
            } else {
690
            }
691

    
692
            if (this.views[view_id].vms_view) {
693
                this.views[view_id].metadata_view = this.metadata_view;
694
            }
695
            return this.views[view_id];
696
        },
697
            
698
        hide_views: function(skip) {
699
            _.each(this.views, function(view) {
700
                if (skip.indexOf(view) === -1) {
701
                    $(view.el).hide();
702
                }
703
            }, this)
704
        },
705
        
706
        get: function(view_id) {
707
            return this.views[view_id];
708
        },
709
        
710
        session_view: function() {
711
            if (this.pane_view_from_session() > 0) {
712
                return this.views_pane_indexes[this.pane_view_from_session()];
713
            } else {
714
                return this.views_indexes[this.machine_view_from_session()];
715
            }
716
        },
717

    
718
        pane_view_from_session: function() {
719
            return $.cookie("pane_view") || 0;
720
        },
721

    
722
        machine_view_from_session: function() {
723
            return $.cookie("machine_view") || 0;
724
        },
725

    
726
        update_session: function() {
727
            $.cookie("pane_view", this.pane_ids[this.current_view_id]);
728
            if (this.current_view.vms_view) {
729
                $.cookie("machine_view", this.views_ids[this.current_view_id]);
730
            }
731
        },
732

    
733
        identify_view: function(view_id) {
734
            // machines view_id is an alias to
735
            // one of the 3 machines view
736
            // identify which one (if no cookie set defaults to icon)
737
            if (view_id == "machines") {
738
                var index = this.machine_view_from_session();
739
                view_id = this.views_indexes[index];
740
            }
741
            return view_id;
742
        },
743
        
744
        // switch to current view pane
745
        // if differs from the visible one
746
        show_view_pane: function() {
747
            if (this.current_view.pane != this.current_pane) {
748
                $(this.current_view.pane).show();
749
                $(this.current_pane).hide();
750
                this.current_pane = this.current_view.pane;
751
            }
752
        },
753
        
754
        show_view: function(view_id) {
755
            //var d = new Date;
756
            var ret = this._show_view(view_id);
757
            //console.log((new Date)-d)
758
            return ret;
759
        },
760

    
761
        _show_view: function(view_id) {
762
            try {
763
                // same view, visible
764
                // get out of here asap
765
                if (this.current_view && 
766
                    this.current_view.id == view_id && 
767
                    this.current_view.visible()) {
768
                    return;
769
                }
770
                
771
                // choose proper view_id
772
                view_id = this.identify_view(view_id);
773

    
774
                // add/create view and update current view
775
                var view = this.add_view(view_id);
776
                
777
                // set current view
778
                this.current_view = view;
779
                this.current_view_id = view_id;
780

    
781
                // hide all other views
782
                this.hide_views([this.current_view]);
783
                
784
                // FIXME: depricated
785
                $(".large-spinner").remove();
786

    
787
                storage.vms.reset_pending_actions();
788
                storage.vms.stop_stats_update();
789

    
790
                // show current view
791
                this.show_view_pane();
792
                view.show();
793
                
794
                // update menus
795
                if (this.select_view) {
796
                    this.select_view.update_layout();
797
                }
798
                this.current_view.__update_layout();
799

    
800
                // update cookies
801
                this.update_session();
802
                
803
                // machines view subnav
804
                if (this.current_view.vms_view) {
805
                    $("#machines-pane").show();
806
                } else {
807
                    $("#machines-pane").hide();
808
                }
809
                
810
                // fix footer position
811
                // TODO: move footer handlers in
812
                // main view (from home.html)
813
                if (window.positionFooter) {
814
                    window.positionFooter();
815
                }
816

    
817
                // trigger view change event
818
                this.trigger("view:change", this.current_view.view_id);
819
                this.select_view.title.text(this.get_title());
820
                $(window).trigger("view:change");
821
                return view;
822
            } catch (err) {
823
                snf.ui.trigger_error(-2, "Cannot show view: " + view_id, err);
824
            }
825
        },
826

    
827
        reset_vm_actions: function() {
828
        
829
        },
830
        
831
        // identify current view
832
        // based on views element visibility
833
        current_view_id: function() {
834
            var found = false;
835
            _.each(this.views, function(key, value) {
836
                if (value.visible()) {
837
                    found = value;
838
                }
839
            })
840
            return found;
841
        }
842

    
843
    });
844

    
845
    snf.ui.main = new views.MainView();
846
    
847
    snf.ui.logout = function() {
848
        $.cookie("X-Auth-Token", null);
849
        if (snf.config.logout_url !== undefined)
850
        {
851
            window.location = snf.config.logout_url;
852
        } else {
853
            window.location.reload();
854
        }
855
    }
856

    
857
    snf.ui.init = function() {
858
        if (snf.config.handle_window_exceptions) {
859
            window.onerror = function(msg, file, line) {
860
                snf.ui.trigger_error("CRITICAL", msg, {}, { file:file + ":" + line, allow_close: false });
861
            };
862
        }
863
        snf.ui.main.load();
864
    }
865

    
866
})(this);