Statistics
| Branch: | Tag: | Revision:

root / ui / static / snf / js / ui / web / ui_main_view.js @ 9ffd10ce

History | View | Annotate | Download (26.8 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
    // TODO: implement me
19
    views.NoticeView = views.Overlay.extend({});
20

    
21
    views.MultipleActionsView = views.View.extend({
22
        view_id: "multiple_actions",
23

    
24
        _actions: {},
25
        el: '#multiple_actions_container',
26
        
27
        initialize: function() {
28
            this.actions = {};
29
            views.MultipleActionsView.__super__.initialize.call(this);
30
            
31
            // view elements
32
            this.confirm_actions = this.$(".confirm_multiple_actions");
33
            this.confirm_actions_yes = this.$(".confirm_multiple_actions button.yes");
34
            this.confirm_actions_no = this.$(".confirm_multiple_actions button.no");
35
            this.confirm_reboot = this.$(".confirm_reboot_required");
36
            this.confirm_reboot_yes = this.$(".confirm_reboot_required button.yes");
37
            this.confirm_reboot_no = this.$(".confirm_reboot_required button.no");
38
            this.confirm_reboot_list = this.confirm_reboot.find(".reboot-machines-list");
39

    
40
            this.init_handlers();
41
            this.update_layout();
42
            
43
            // for heavy resize/scroll window events
44
            // do it `like a boss` 
45
            this.fix_position = _.throttle(this.fix_position, 100);
46
            this.update_layout = _.throttle(this.update_layout, 100);
47
            this.show_limit = 1;
48
        },
49

    
50
        init_handlers: function() {
51
            var self = this;
52

    
53
            $(window).resize(_.bind(function(){
54
                this.fix_position();
55
            }, this));
56

    
57
            $(window).scroll(_.bind(function(){
58
                this.fix_position();
59
            }, this));
60
            
61
            // confirm/cancel button handlers
62
            var self = this;
63
            this.confirm_actions_yes.click(function(){ self.do_all(); })
64
            this.confirm_actions_no.click(function(){
65
                self.reset_actions();
66
            });
67

    
68
            this.confirm_reboot_yes.click(function(){ self.do_reboot_all(); })
69
            this.confirm_reboot_no.click(function(){
70
                self.reset_reboots();
71
            });
72

    
73
            storage.vms.bind("change:pending_action", _.bind(this.handle_vm_change, this));
74
            storage.vms.bind("change:reboot_required", _.bind(this.handle_vm_change, this));
75

    
76
        },
77

    
78
        handle_vm_change: function(vm) {
79
            if (vm.has_pending_action()) {
80
                var action = vm.get("pending_action");
81
                this.add_action(vm, action);
82
            } else {
83
                this.remove_action(vm);
84
            }
85
            this.update_layout();
86
        },
87

    
88
        add_action: function(vm, action) {
89
            this._actions[vm.id] = {'vm': vm, 'action': action};
90
        },
91

    
92
        remove_action: function(vm) {
93
            delete this._actions[vm.id];
94
        },
95

    
96
        reset: function() {
97
            this._actions = {};
98
            this.update_layout();
99
        },
100
        
101
        reboot_vm: function(vm) {
102
            vm.call("reboot");
103
        },
104

    
105
        do_reboot_all: function() {
106
            _.each(storage.vms.get_reboot_required(), function(vm){
107
                this.reboot_vm(vm)
108
            }, this)  
109
        },
110

    
111
        do_all: function() {
112
            _.each(this._actions, function(action){
113
                action.vm.call(action.action);
114
            }, this)  
115
            this.reset_actions();
116
        },
117

    
118
        reset_reboots: function () {
119
            _.each(storage.vms.get_reboot_required(), function(vm) {vm.set({'reboot_required': false})}, this);
120
            this.update_layout();
121
        },
122

    
123
        reset_actions: function() {
124
            _.each(this._actions, _.bind(function(action){
125
                try {
126
                    action.vm.clear_pending_action();
127
                    this.remove_action(action.vm);
128
                } catch(err) {
129
                    console.error("vm " + action.vm.id + " failed to reset", err);
130
                }
131
            }, this))  
132
        },
133
        
134
        fix_position: function() {
135
            $('.confirm_multiple').removeClass('fixed');
136
            if (($(this.el).offset().top +$(this.el).height())> ($(window).scrollTop() + $(window).height())) {
137
                $('.confirm_multiple').addClass('fixed');
138
            }
139
        },
140
        
141
        check_notify_limit: function() {
142
            this.show_limit = 1;
143
            if (ui.main.current_view && ['networks', 'vm_list'].indexOf(ui.main.current_view.view_id) > -1) {
144
                this.show_limit = 0;
145
            }
146
        },
147
        
148
        update_reboot_required_list: function(vms) {
149
            this.confirm_reboot_list.empty();
150
        },
151

    
152
        update_reboot_required: function() {
153
            var vms = storage.vms.get_reboot_required();
154
            if (vms.length) {
155
                this.confirm_reboot.find(".actionLen").text(vms.length);
156
                this.update_reboot_required_list();
157
                this.confirm_reboot.show();
158
                $(this.el).show();
159
            } else {
160
                if (!this.actions_visible) {
161
                   $(this.el).hide();
162
                }
163
                this.confirm_reboot.hide();
164
            }
165
        },
166

    
167
        update_layout: function() {
168
            this.check_notify_limit();
169
            this.actions_visible = false;
170

    
171
            if (_.size(this._actions) > this.show_limit) {
172
                this.actions_visible = true;
173
                $(this.el).show();
174
                this.confirm_actions.show();
175
            } else {
176
                $(this.el).hide();
177
                this.confirm_actions.hide();
178
            }
179

    
180
            this.update_reboot_required();
181
            this.confirm_actions.find(".actionLen").text(_.size(this._actions));
182
            $(window).trigger("resize");
183
        }
184
    })
185
    
186
    // menu wrapper view
187
    views.SelectView = views.View.extend({
188
        
189
        initialize: function(view) {
190
            this.parent = view;
191

    
192
            this.pane_view_selector = $(".css-tabs");
193
            this.machine_view_selector = $("#view-select");
194
            this.el = $(".css-tabs");
195
            this.title = $(".tab-name");
196

    
197
            this.set_handlers();
198
            this.update_layout();
199

    
200
            views.SelectView.__super__.initialize.apply(this, arguments);
201
        },
202
        
203
        clear_active: function() {
204
            this.pane_view_selector.find("a").removeClass("active");
205
            this.machine_view_selector.find("a").removeClass("activelink");
206
        },
207
        
208
        // intercept menu links
209
        set_handlers: function() {
210
            var self = this;
211
            this.pane_view_selector.find("a").hover(function(){
212
                // FIXME: title from href ? omg
213
                self.title.text($(this).attr("href"));
214
            }, function(){
215
                self.title.text(self.parent.get_title());
216
            });
217

    
218
            this.pane_view_selector.find("a#machines_view_link").click(_.bind(function(ev){
219
                ev.preventDefault();
220
                this.parent.show_view("machines");
221
            }, this))
222
            this.pane_view_selector.find("a#networks_view_link").click(_.bind(function(ev){
223
                ev.preventDefault();
224
                this.parent.show_view("networks");
225
            }, this))
226
            this.pane_view_selector.find("a#disks_view_link").click(_.bind(function(ev){
227
                ev.preventDefault();
228
                this.parent.show_view("disks");
229
            }, this))
230
            
231
            this.machine_view_selector.find("a#machines_view_icon_link").click(_.bind(function(ev){
232
                ev.preventDefault();
233
                var d = $.now();
234
                this.parent.show_view("icon");
235
            }, this))
236
            this.machine_view_selector.find("a#machines_view_list_link").click(_.bind(function(ev){
237
                ev.preventDefault();
238
                this.parent.show_view("list");
239
            }, this))
240
            this.machine_view_selector.find("a#machines_view_single_link").click(_.bind(function(ev){
241
                ev.preventDefault();
242
                this.parent.show_view("single");
243
            }, this))
244
        },
245

    
246
        update_layout: function() {
247
            this.clear_active();
248

    
249
            var pane_index = this.parent.pane_ids[this.parent.current_view_id];
250
            $(this.pane_view_selector.find("a")).removeClass("active");
251
            $(this.pane_view_selector.find("a").get(pane_index)).addClass("active");
252
            
253
            if (this.parent.current_view && this.parent.current_view.vms_view) {
254

    
255
                if (storage.vms.length > 0) {
256
                    this.machine_view_selector.show();
257
                    var machine_index = this.parent.views_ids[this.parent.current_view_id];
258
                    $(this.machine_view_selector.find("a").get(machine_index)).addClass("activelink");
259
                } else {
260
                    this.machine_view_selector.hide();
261
                }
262
            } else {
263
                this.machine_view_selector.hide();
264
            }
265

    
266
        }
267
    });
268

    
269
    views.MainView = views.View.extend({
270
        el: 'body',
271
        view_id: 'main',
272
        
273
        // FIXME: titles belong to SelectView
274
        views_titles: {
275
            'icon': 'machines', 'single': 'machines', 
276
            'list': 'machines', 'networks': 'networks',
277
            'disks': 'disks'
278
        },
279

    
280
        // indexes registry
281
        views_indexes: {0: 'icon', 2:'single', 1: 'list', 3:'networks'},
282
        views_pane_indexes: {0:'single', 1:'networks', 2:'disks'},
283

    
284
        // views classes registry
285
        views_classes: {'icon': views.IconView, 'single': views.SingleView, 
286
            'list': views.ListView, 'networks': views.NetworksView},
287

    
288
        // view ids
289
        views_ids: {'icon':0, 'single':2, 'list':1, 'networks':3},
290

    
291
        // on which pane id each view exists
292
        // machine views (icon,single,list) are all on first pane
293
        pane_ids: {'icon':0, 'single':0, 'list':0, 'networks':1, 'disks':2},
294
    
295
        initialize: function(show_view) {
296
            if (!show_view) { show_view = 'icon' };
297
            
298
            // fallback to browser error reporting (true for debug)
299
            this.skip_errors = true
300

    
301
            // reset views
302
            this.views = {};
303

    
304
            this.el = $("#app");
305
            // reset main view status
306
            this._loaded = false;
307
            this.status = "Initializing...";
308

    
309
            // initialize handlers
310
            this.init_handlers();
311

    
312
            // identify initial view from user cookies
313
            // this view will be visible after loading of
314
            // main view
315
            this.initial_view = this.session_view();
316

    
317
            views.MainView.__super__.initialize.call(this);
318

    
319
            $(window).focus(_.bind(this.handle_window_focus, this, "focus"));
320
            $(window).blur(_.bind(this.handle_window_focus, this, "out"));
321

    
322
            this.focused = true;
323
        },
324

    
325
        handle_window_focus: function(focus) {
326
            if (!snf.config.delay_on_blur) { return };
327

    
328
            if (focus === "focus") {
329
                this.focused = true;
330
                this.set_interval_timeouts(snf.config.update_interval);
331
            } else {
332
                this.focused = false;
333
                this.set_interval_timeouts(snf.config.update_interval*3);
334
            }
335
        },
336

    
337
        set_interval_timeouts: function(time) {
338
            _.each([this._networks, this._vms], function(fetcher){
339
                if (!fetcher) { return };
340
                fetcher.timeout = time;
341
                fetcher.stop().start();
342
            })
343
        },
344
        
345
        vms_handlers_registered: false,
346

    
347
        // register event handlers
348
        // 
349
        // vms storage events to identify if vms list 
350
        // is empty and display empty view if user viewing
351
        // a machine view
352
        //
353
        // api/ui error event handlers
354
        init_handlers: function() {
355
            // vm handlers
356
            storage.vms.bind("remove", _.bind(this.check_empty, this));
357
            storage.vms.bind("add", _.bind(this.check_empty, this));
358
            storage.vms.bind("change", _.bind(this.check_empty, this));
359
            storage.vms.bind("reset", _.bind(this.check_empty, this));
360
            
361
            // api calls handlers
362
            synnefo.api.bind("error", _.bind(this.handle_api_error, this));
363
            synnefo.api.bind("change:error_state", _.bind(this.handle_api_error_state, this));
364
            synnefo.ui.bind("error", _.bind(this.handle_ui_error, this));
365
        },
366
        
367
        handle_api_error_state: function(state) {
368
            if (snf.api.error_state === snf.api.STATES.ERROR) {
369
                this.stop_intervals();
370
            } else {
371
                if (this.intervals_stopped) {
372
                    this.update_intervals();
373
                }
374
            }
375
        },
376
        
377
        handle_api_error: function(args) {
378
            if (arguments.length == 1) { arguments = _.toArray(arguments[0])};
379

    
380
            if (!_.last(arguments).display) {
381
                return;
382
            }
383

    
384
            this.error_state = true;
385
            
386
            var xhr = arguments[0];
387
            var args = util.parse_api_error.apply(util, arguments);
388
            
389
            // force logout if UNAUTHORIZED request arrives
390
            if (args.code == 401) { snf.ui.logout(); return };
391

    
392
            var error_entry = [args.ns, args.code, args.message, args.type, args.details, args];
393
            this.error_view.show_error.apply(this.error_view, error_entry);
394
        },
395

    
396
        handle_ui_error: function(data) {
397
            var msg = data.msg, code = data.code, err_obj = data.error;
398
            error = msg + "<br /><br />" + snf.util.stacktrace().replace("at", "<br /><br />at");
399
            params = { title: "UI error", extra_details: data.extra };
400
            params.allow_close = data.extra.allow_close === undefined ? true : data.extra.allow_close;
401
            this.error_view.show_error("UI", -1, msg, "JS Exception", error, params);
402
        },
403

    
404
        init_overlays: function() {
405
            this.create_vm_view = new views.CreateVMView();
406
            //this.notice_view = new views.NoticeView();
407
        },
408
        
409
        show_loading_view: function() {
410
            $("#container #content").hide();
411
            $("#loading-view").show();
412
        },
413

    
414
        hide_loading_view: function() {
415
            $("#container #content").show();
416
            $("#loading-view").hide();
417
            $(".css-panes").show();
418
        },
419
        
420
        items_to_load: 4,
421
        completed_items: 0,
422
        check_status: function(loaded) {
423
            this.completed_items++;
424
            // images, flavors loaded
425
            if (this.completed_items == 2) {
426
                this.load_nets_and_vms();
427
            }
428
            if (this.completed_items == this.items_to_load) {
429
                this.after_load();
430
            }
431
        },
432

    
433
        load_nets_and_vms: function() {
434
            var self = this;
435
            this.update_status("Loading vms...");
436
            storage.vms.fetch({refresh:true, update:false, success: function(){
437
                self.update_status("VMS Loaded.");
438
                self.check_status();
439
            }});
440

    
441
            this.update_status("Loading networks...");
442
            storage.networks.fetch({refresh:true, update:false, success: function(){
443
                self.update_status("Networks loaded.");
444
                self.check_status();
445
            }});
446
        },  
447

    
448
        init_intervals: function() {
449
            this._networks = storage.networks.get_fetcher(snf.config.update_interval, 
450
                                                          snf.config.update_interval / 2, 
451
                                                          1, true, undefined);
452
            this._vms = storage.vms.get_fetcher(snf.config.update_interval, 
453
                                                snf.config.update_interval / 2, 
454
                                                1, true, undefined);
455
        },
456

    
457
        stop_intervals: function() {
458
            if (this._networks) { this._networks.stop(); }
459
            if (this._vms) { this._vms.stop(); }
460
            this.intervals_stopped = true;
461
        },
462

    
463
        update_intervals: function() {
464
            if (this._networks) {
465
                this._networks.stop();
466
                this._networks.start();
467
            } else {
468
                this.init_intervals();
469
            }
470

    
471
            if (this._vms) {
472
                this._vms.stop();
473
                this._vms.start();
474
            } else {
475
                this.init_intervals();
476
            }
477

    
478
            this.intervals_stopped = false;
479
        },
480

    
481
        after_load: function() {
482
            this.update_status("Setting vms update interval...");
483
            this.init_intervals();
484
            this.update_intervals();
485
            this.update_status("Loaded");
486
            // FIXME: refactor needed
487
            // initialize views
488
            this.initialize_views()
489
            this.update_status("Initializing overlays...");
490
            this.init_overlays();
491
            // display initial view
492
            this.loaded = true;
493
            this.show_initial_view();
494
            this.check_empty();
495
        },
496

    
497
        load: function() {
498
            this.error_view = new views.ErrorView();
499
            this.feedback_view = new views.FeedbackView();
500
            var self = this;
501
            // initialize overlay views
502
            
503
            // display loading message
504
            this.show_loading_view();
505
            // sync load initial data
506
            this.update_status("Loading images...");
507
            storage.images.fetch({refresh:true, update:false, success: function(){
508
                self.check_status()
509
            }});
510
            this.update_status("Loading flavors...");
511
            storage.flavors.fetch({refresh:true, update:false, success:function(){
512
                self.check_status()
513
            }});
514
        },
515

    
516
        update_status: function(msg) {
517
            this.log.debug(msg)
518
            this.status = msg;
519
            $("#loading-view .info").removeClass("hidden")
520
            $("#loading-view .info").text(this.status);
521
        },
522

    
523
        initialize_views: function() {
524
            this.empty_view = new views.EmptyView();
525
            this.select_view = new views.SelectView(this);
526
            this.metadata_view = new views.MetadataView();
527
            this.multiple_actions_view = new views.MultipleActionsView();
528
            
529
            this.add_view("icon");
530
            this.add_view("list");
531
            this.add_view("single");
532
            this.add_view("networks");
533

    
534
            this.init_menu();
535
        },
536

    
537
        init_menu: function() {
538
            $(".usermenu .feedback").click(_.bind(function(){
539
                this.feedback_view.show();
540
            }, this));
541
        },
542
        
543
        // initial view based on user cookie
544
        show_initial_view: function() {
545
          this.set_vm_view_handlers();
546
          this.hide_loading_view();
547
          this.show_view(this.initial_view);
548
          this.trigger("initial");
549
        },
550

    
551
        show_vm_details: function(vm) {
552
            snf.ui.main.show_view("single")
553
            snf.ui.main.current_view.show_vm(vm);
554
        },
555

    
556
        set_vm_view_handlers: function() {
557
            $("#createcontainer #create").click(_.bind(function(){
558
                this.create_vm_view.show();
559
            }, this))
560
        },
561

    
562
        check_empty: function() {
563
            if (!this.loaded) { return }
564
            if (storage.vms.length == 0) {
565
                this.show_view("machines");
566
                this.show_empty();
567
            } else {
568
                this.hide_empty();
569
            }
570
            this.select_view.update_layout();
571
        },
572

    
573
        show_empty: function() {
574
            $("#machines-pane-top").addClass("empty");
575

    
576
            this.$(".panes").hide();
577
            this.$("#machines-pane").show();
578

    
579
            this.hide_views([]);
580
            this.empty_view.show();
581
        },
582

    
583
        hide_empty: function() {
584
            $("#machines-pane-top").removeClass("empty");
585

    
586
            this.empty_view = new views.EmptyView();
587
            this.empty_view.hide();
588
            if (this.current_view && !this.current_view.visible()) { 
589
                this.current_view.show(); 
590
            }
591
        },
592
        
593
        get_title: function(view_id) {
594
            var view_id = view_id || this.current_view_id;
595
            return this.views_titles[view_id];
596
        },
597

    
598
        // return class object for the given view or false if
599
        // the view is not registered
600
        get_class_for_view: function (view_id) {
601
            if (!this.views_classes[view_id]) {
602
                return false;
603
            }
604
            return this.views_classes[view_id];
605
        },
606

    
607
        view: function(view_id) {
608
            return this.views[view_id];
609
        },
610

    
611
        add_view: function(view_id) {
612
            if (!this.views[view_id]) {
613
                var cls = this.get_class_for_view(view_id);
614
                if (this.skip_errors) {
615
                    this.views[view_id] = new cls();
616
                    $(this.views[view_id]).bind("resize", _.bind(function() {
617
                        window.positionFooter();
618
                        this.multiple_actions_view.fix_position();
619
                    }, this));
620
                } else {
621
                    // catch ui errors
622
                    try {
623
                        this.views[view_id] = new cls();
624
                        $(this.views[view_id]).bind("resize", _.bind(function() {
625
                            window.positionFooter();
626
                            this.multiple_actions_view.fix_position();
627
                        }, this));
628
                    } catch (err) {snf.ui.trigger_error(-1, "Cannot add view", err)}
629
                }
630
            } else {
631
            }
632

    
633
            if (this.views[view_id].vms_view) {
634
                this.views[view_id].metadata_view = this.metadata_view;
635
            }
636
            return this.views[view_id];
637
        },
638
            
639
        hide_views: function(skip) {
640
            _.each(this.views, function(view) {
641
                if (skip.indexOf(view) === -1) {
642
                    $(view.el).hide();
643
                }
644
            }, this)
645
        },
646
        
647
        get: function(view_id) {
648
            return this.views[view_id];
649
        },
650
        
651
        session_view: function() {
652
            if (this.pane_view_from_session() > 0) {
653
                return this.views_pane_indexes[this.pane_view_from_session()];
654
            } else {
655
                return this.views_indexes[this.machine_view_from_session()];
656
            }
657
        },
658

    
659
        pane_view_from_session: function() {
660
            return $.cookie("pane_view") || 0;
661
        },
662

    
663
        machine_view_from_session: function() {
664
            return $.cookie("machine_view") || 0;
665
        },
666

    
667
        update_session: function() {
668
            $.cookie("pane_view", this.pane_ids[this.current_view_id]);
669
            if (this.current_view.vms_view) {
670
                $.cookie("machine_view", this.views_ids[this.current_view_id]);
671
            }
672
        },
673

    
674
        identify_view: function(view_id) {
675
            // machines view_id is an alias to
676
            // one of the 3 machines view
677
            // identify which one (if no cookie set defaults to icon)
678
            if (view_id == "machines") {
679
                var index = this.machine_view_from_session();
680
                view_id = this.views_indexes[index];
681
            }
682
            return view_id;
683
        },
684
        
685
        // switch to current view pane
686
        // if differs from the visible one
687
        show_view_pane: function() {
688
            if (this.current_view.pane != this.current_pane) {
689
                $(this.current_view.pane).show();
690
                $(this.current_pane).hide();
691
                this.current_pane = this.current_view.pane;
692
            }
693
        },
694

    
695
        show_view: function(view_id) {
696
            try {
697
                // same view, visible
698
                // get out of here asap
699
                if (this.current_view && 
700
                    this.current_view.id == view_id && 
701
                    this.current_view.visible()) {
702
                    return;
703
                }
704
                
705
                // choose proper view_id
706
                view_id = this.identify_view(view_id);
707

    
708
                // add/create view and update current view
709
                var view = this.add_view(view_id);
710
                
711
                // set current view
712
                this.current_view = view;
713
                this.current_view_id = view_id;
714

    
715
                // hide all other views
716
                this.hide_views([this.current_view]);
717
                
718
                // FIXME: depricated
719
                $(".large-spinner").remove();
720

    
721
                storage.vms.reset_pending_actions();
722
                storage.vms.stop_stats_update();
723

    
724
                // show current view
725
                this.show_view_pane();
726
                view.show();
727
                
728
                // update menus
729
                if (this.select_view) {
730
                    this.select_view.update_layout();
731
                }
732
                this.current_view.__update_layout();
733

    
734
                // update cookies
735
                this.update_session();
736
                
737
                // machines view subnav
738
                if (this.current_view.vms_view) {
739
                    $("#machines-pane").show();
740
                } else {
741
                    $("#machines-pane").hide();
742
                }
743
                
744
                // fix footer position
745
                // TODO: move footer handlers in
746
                // main view (from home.html)
747
                if (window.positionFooter) {
748
                    window.positionFooter();
749
                }
750

    
751
                // trigger view change event
752
                this.trigger("view:change", this.current_view.view_id);
753
                $(window).trigger("view:change");
754
                return view;
755
            } catch (err) {
756
                snf.ui.trigger_error(-2, "Cannot show view: " + view_id, err);
757
            }
758
        },
759

    
760
        reset_vm_actions: function() {
761
        
762
        },
763
        
764
        // identify current view
765
        // based on views element visibility
766
        current_view_id: function() {
767
            var found = false;
768
            _.each(this.views, function(key, value) {
769
                if (value.visible()) {
770
                    found = value;
771
                }
772
            })
773
            return found;
774
        }
775

    
776
    });
777

    
778
    snf.ui.main = new views.MainView();
779
    
780
    snf.ui.logout = function() {
781
        $.cookie("X-Auth-Token", null);
782
        if (window.LOGOUT_REDIRECT !== undefined)
783
        {
784
            window.location = window.LOGOUT_REDIRECT;
785
        } else {
786
            window.location.reload();
787
        }
788
    }
789

    
790
    snf.ui.init = function() {
791
        window.onerror = function(msg, file, line) {
792
            snf.ui.trigger_error("CRITICAL", msg, {}, { file:file + ":" + line, allow_close: false });
793
        };
794
        snf.ui.main.load();
795
    }
796

    
797
})(this);