Statistics
| Branch: | Tag: | Revision:

root / snf-app / synnefo / ui / static / snf / js / ui / web / ui_main_view.js @ 30b6f316

History | View | Annotate | Download (33.9 kB)

1
// Copyright 2011 GRNET S.A. All rights reserved.
2
// 
3
// Redistribution and use in source and binary forms, with or
4
// without modification, are permitted provided that the following
5
// conditions are met:
6
// 
7
//   1. Redistributions of source code must retain the above
8
//      copyright notice, this list of conditions and the following
9
//      disclaimer.
10
// 
11
//   2. Redistributions in binary form must reproduce the above
12
//      copyright notice, this list of conditions and the following
13
//      disclaimer in the documentation and/or other materials
14
//      provided with the distribution.
15
// 
16
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
// POSSIBILITY OF SUCH DAMAGE.
28
// 
29
// The views and conclusions contained in the software and
30
// documentation are those of the authors and should not be
31
// interpreted as representing official policies, either expressed
32
// or implied, of GRNET S.A.
33
// 
34

    
35
;(function(root){
36

    
37
    // root
38
    var root = root;
39
    
40
    // setup namepsaces
41
    var snf = root.synnefo = root.synnefo || {};
42
    var models = snf.models = snf.models || {}
43
    var storage = snf.storage = snf.storage || {};
44
    var ui = snf.ui = snf.ui || {};
45

    
46
    var views = snf.views = snf.views || {}
47

    
48
    // shortcuts
49
    var bb = root.Backbone;
50
    var util = snf.util;
51
    
52
    views.ApiInfoView = views.Overlay.extend({
53
        view_id: "api_info_view",
54
        
55
        content_selector: "#api-info-overlay",
56
        css_class: 'overlay-api-info overlay-info',
57
        overlay_id: "overlay-api-info",
58

    
59
        subtitle: "",
60
        title: "API Access",
61

    
62
        beforeOpen: function() {
63
            var cont = this.$(".copy-content p");
64
            var token = $.cookie("X-Auth-Token");
65

    
66
            cont.html("");
67
            cont.text(token);
68
            
69
            this.cont = cont;
70
            this.token = token;
71
            try { delete this.clip; } catch (err) {};
72
        },
73

    
74
        onOpen: function() {
75
            views.ApiInfoView.__super__.onOpen(this, arguments);
76
            this.clip = new snf.util.ClipHelper(this.cont.parent(), this.token);
77
        },
78

    
79
        onClose: function() {
80
            var cont = this.$(".copy-content p");
81
            var token = $.cookie("X-Auth-Token");
82
            cont.html("");
83
        }
84
    });
85

    
86
    // TODO: implement me
87
    views.NoticeView = views.Overlay.extend({});
88

    
89
    views.MultipleActionsView = views.View.extend({
90
        view_id: "multiple_actions",
91

    
92
        _actions: {},
93
        el: '#multiple_actions_container',
94
        
95
        initialize: function() {
96
            this.actions = {};
97
            this.ns_config = {};
98

    
99
            views.MultipleActionsView.__super__.initialize.call(this);
100

    
101
            this.ns_tpl = this.$(".confirm_multiple_actions-template").clone()
102

    
103
            this.init_handlers();
104
            this.update_layout();
105
            
106
            // for heavy resize/scroll window events
107
            // do it `like a boss` 
108
            this.fix_position = _.throttle(this.fix_position, 100);
109
            this.update_layout = _.throttle(this.update_layout, 100);
110
            this.show_limit = 1;
111

    
112
            this.init_ns("vms", {
113
                msg_tpl:"Your actions will affect 1 machine",
114
                msg_tpl_plural:"Your actions will affect {0} machines",
115
                actions_msg: {confirm: "Confirm all", cancel: "Cancel all"},
116
                limit: 1,
117
                cancel_all: function() { snf.storage.vms.reset_pending_actions(); },
118
                do_all: function() { snf.storage.vms.do_all_pending_actions(); }
119
            });
120
            
121
            this.init_ns("nets", {
122
                msg_tpl:"Your actions will affect 1 private network",
123
                msg_tpl_plural:"Your actions will affect {0} private networks",
124
                actions_msg: {confirm: "Confirm all", cancel: "Cancel all"},
125
                limit: 1,
126
                cancel_all: function() { snf.storage.networks.reset_pending_actions(); },
127
                do_all: function() { snf.storage.networks.do_all_pending_actions(); }
128
            });
129

    
130
            this.init_ns("reboots", {
131
                msg_tpl:"1 machine needs to be rebooted for changes to apply.",
132
                msg_tpl_plural:"{0} machines needs to be rebooted for changes to apply.",
133
                actions_msg: {confirm: "Reboot all", cancel: "Cancel all"},
134
                limit: 0,
135
                cancel_all: function() { snf.storage.vms.reset_reboot_required(); },
136
                do_all: function() { snf.storage.vms.do_all_reboots(); }
137
            });
138
        },
139
        
140
        init_ns: function(ns, params) {
141
            this.actions[ns] = {};
142
            var nsconf = this.ns_config[ns] = params || {};
143
            nsconf.cont = $(this.$("#conirm_multiple_cont_template").clone());
144
            nsconf.cont.attr("id", "confirm_multiple_cont_" + ns);
145
            $(this.el).find(".ns-confirms-cont").append(nsconf.cont).addClass(ns);
146
            $(this.el).find(".ns-confirms-cont").append(nsconf.cont).addClass("confirm-ns");
147
            nsconf.cont.find(".msg button.yes").text(
148
                nsconf.actions_msg.confirm).click(_.bind(this.do_all, this, ns));
149
            nsconf.cont.find(".msg button.no").text(
150
                nsconf.actions_msg.cancel).click(_.bind(this.cancel_all, this, ns));
151
        },
152

    
153
        do_all: function(ns) {
154
            this.ns_config[ns].do_all();
155
        },
156

    
157
        cancel_all: function(ns) {
158
            this.ns_config[ns].cancel_all();
159
        },
160

    
161
        init_handlers: function() {
162
            var self = this;
163

    
164
            $(window).resize(_.bind(function(){
165
                this.fix_position();
166
            }, this));
167

    
168
            $(window).scroll(_.bind(function(){
169
                this.fix_position();
170
            }, this));
171

    
172
            storage.vms.bind("change:pending_action", _.bind(this.handle_action_add, this, "vms"));
173
            storage.vms.bind("change:reboot_required", _.bind(this.handle_action_add, this, "reboots"));
174
            storage.networks.bind("change:actions", _.bind(this.handle_action_add, this, "nets"));
175
        },
176

    
177
        handle_action_add: function(type, model, action) {
178
            var actions = this.actions[type];
179
            
180
            // TODO: remove type specific addition code in its own namespace
181
            if (type == "nets") {
182
                if (!action || action.is_empty()) {
183
                    delete actions[model.id];
184
                } else {
185
                    actions[model.id] = {model: model, actions: action.actions};
186
                }
187
            }
188

    
189
            if (type == "vms") {
190
                _.each(actions, function(action) {
191
                    if (action.model.id == model.id) {
192
                        delete actions[action]
193
                    }
194
                });
195

    
196
                var actobject = {};
197
                actobject[action] = [[]];
198
                actions[model.id] = {model: model, actions: actobject};
199
                if (typeof action == "undefined") {
200
                    delete actions[model.id]
201
                }
202
            }
203

    
204
            if (type == "reboots") {
205
                _.each(actions, function(action) {
206
                    if (action.model.id == model.id) {
207
                        delete actions[action]
208
                    }
209
                });
210
                var actobject = {};
211
                actobject['reboot'] = [[]];
212
                actions[model.id] = {model: model, actions: actobject};
213
                if (!action) {
214
                    delete actions[model.id]
215
                }
216
            }
217
            
218
            this.update_layout();
219
        },
220

    
221
        update_actions_content: function(ns) {
222
            var conf = this.ns_config[ns];
223
            conf.cont.find(".details").empty();
224
            conf.cont.find(".msg p").text("");
225
            
226
            var count = 0;
227
            var actionscount = 0;
228
            _.each(this.actions[ns], function(actions, model_id) {
229
                count++;
230
                _.each(actions.actions, function(params, act_name){
231
                    if (params && params.length) {
232
                        actionscount += params.length;
233
                    } else {
234
                        actionscount++;
235
                    }
236
                })
237
                this.total_confirm_actions++;
238
            });
239
            
240
            var limit = conf.limit;
241
            if (ui.main.current_view.view_id == "vm_list") {
242
                limit = 0;
243
            }
244

    
245
            if (actionscount > limit) {
246
                conf.cont.show();
247
                this.confirm_ns_open++;
248
            } else {
249
                conf.cont.hide();
250
            }
251
            
252
            var msg = count > 1 ? conf.msg_tpl_plural : conf.msg_tpl;
253
            conf.cont.find(".msg p").text(msg.format(count));
254

    
255
            return conf.cont;
256
        },
257

    
258
        fix_position: function() {
259
            $('.confirm_multiple').removeClass('fixed');
260
            if (($(this.el).offset().top +$(this.el).height())> ($(window).scrollTop() + $(window).height())) {
261
                $('.confirm_multiple').addClass('fixed');
262
            }
263
        },
264
        
265
        update_layout: function() {
266
            this.confirm_ns_open = 0;
267
            this.total_confirm_actions = 0;
268

    
269
            $(this.el).show();
270
            $(this.el).find("#conirm_multiple_cont_template").hide();
271
            $(this.el).find(".confirm-ns").show();
272
            
273
            _.each(this.ns_config, _.bind(function(params, key) {
274
                this.update_actions_content(key);
275
            }, this));
276

    
277
            if (this.confirm_ns_open > 0) {
278
                $(this.el).show();
279
                this.$(".confirm-all-cont").hide();
280
                this.$(".ns-confirms-cont").show();
281
            } else {
282
                $(this.el).hide();
283
                this.$(".confirm-all-cont").hide();
284
                this.$(".ns-confirms-cont").hide();
285
            }
286

    
287
            $(window).trigger("resize");
288
        }
289
    })
290
    
291
    // menu wrapper view
292
    views.SelectView = views.View.extend({
293
        
294
        initialize: function(view, router) {
295
            this.parent = view;
296
            this.router = router;
297
            this.pane_view_selector = $(".css-tabs");
298
            this.machine_view_selector = $("#view-select");
299
            this.el = $(".css-tabs");
300
            this.title = $(".tab-name");
301

    
302
            this.set_handlers();
303
            this.update_layout();
304

    
305
            views.SelectView.__super__.initialize.apply(this, arguments);
306
        },
307
        
308
        clear_active: function() {
309
            this.pane_view_selector.find("a").removeClass("active");
310
            this.machine_view_selector.find("a").removeClass("activelink");
311
        },
312
        
313
        // intercept menu links
314
        set_handlers: function() {
315
            var self = this;
316
            this.pane_view_selector.find("a").hover(function(){
317
                // FIXME: title from href ? omg
318
                self.title.text($(this).attr("href"));
319
            }, function(){
320
                self.title.text(self.parent.get_title());
321
            });
322

    
323
            this.pane_view_selector.find("a#machines_view_link").click(_.bind(function(ev){
324
                ev.preventDefault();
325
                this.router.vms_index();
326
            }, this))
327
            this.pane_view_selector.find("a#networks_view_link").click(_.bind(function(ev){
328
                ev.preventDefault();
329
                this.router.networks_view();
330
            }, this))
331
            this.pane_view_selector.find("a#disks_view_link").click(_.bind(function(ev){
332
                ev.preventDefault();
333
                this.router.disks_view();
334
            }, this))
335
            
336
            this.machine_view_selector.find("a#machines_view_icon_link").click(_.bind(function(ev){
337
                ev.preventDefault();
338
                var d = $.now();
339
                this.router.vms_icon_view();
340
            }, this))
341
            this.machine_view_selector.find("a#machines_view_list_link").click(_.bind(function(ev){
342
                ev.preventDefault();
343
                this.router.vms_list_view();
344
            }, this))
345
            this.machine_view_selector.find("a#machines_view_single_link").click(_.bind(function(ev){
346
                ev.preventDefault();
347
                this.router.vms_single_view();
348
            }, this))
349
        },
350

    
351
        update_layout: function() {
352
            this.clear_active();
353

    
354
            var pane_index = this.parent.pane_ids[this.parent.current_view_id];
355
            $(this.pane_view_selector.find("a")).removeClass("active");
356
            $(this.pane_view_selector.find("a").get(pane_index)).addClass("active");
357
            
358
            if (this.parent.current_view && this.parent.current_view.vms_view) {
359

    
360
                if (storage.vms.length > 0) {
361
                    this.machine_view_selector.show();
362
                    var machine_index = this.parent.views_ids[this.parent.current_view_id];
363
                    $(this.machine_view_selector.find("a").get(machine_index)).addClass("activelink");
364
                } else {
365
                    this.machine_view_selector.hide();
366
                }
367
            } else {
368
                this.machine_view_selector.hide();
369
            }
370

    
371
        }
372
    });
373

    
374
    views.MainView = views.View.extend({
375
        el: 'body',
376
        view_id: 'main',
377
        
378
        // FIXME: titles belong to SelectView
379
        views_titles: {
380
            'icon': 'machines', 'single': 'machines', 
381
            'list': 'machines', 'networks': 'networks',
382
            'disks': 'disks'
383
        },
384

    
385
        // indexes registry
386
        views_indexes: {0: 'icon', 2:'single', 1: 'list', 3:'networks'},
387
        views_pane_indexes: {0:'single', 1:'networks', 2:'disks'},
388

    
389
        // views classes registry
390
        views_classes: {'icon': views.IconView, 'single': views.SingleView, 
391
            'list': views.ListView, 'networks': views.NetworksView},
392

    
393
        // view ids
394
        views_ids: {'icon':0, 'single':2, 'list':1, 'networks':3},
395

    
396
        // on which pane id each view exists
397
        // machine views (icon,single,list) are all on first pane
398
        pane_ids: {'icon':0, 'single':0, 'list':0, 'networks':1, 'disks':2},
399
    
400
        initialize: function(show_view) {
401
            if (!show_view) { show_view = 'icon' };
402
            
403
            this.router = snf.router;
404
            this.empty_hidden = true;
405
            // fallback to browser error reporting (true for debug)
406
            this.skip_errors = true
407

    
408
            // reset views
409
            this.views = {};
410

    
411
            this.el = $("#app");
412
            // reset main view status
413
            this._loaded = false;
414
            this.status = "Initializing...";
415

    
416
            // initialize handlers
417
            this.init_handlers();
418

    
419
            // identify initial view from user cookies
420
            // this view will be visible after loading of
421
            // main view
422
            this.initial_view = this.session_view();
423

    
424
            views.MainView.__super__.initialize.call(this);
425

    
426
            $(window).focus(_.bind(this.handle_window_focus, this, "focus"));
427
            $(window).blur(_.bind(this.handle_window_focus, this, "out"));
428

    
429
            this.focused = true;
430
        },
431

    
432
        handle_window_focus: function(focus) {
433
            if (!snf.config.delay_on_blur) { return };
434

    
435
            if (focus === "focus") {
436
                this.focused = true;
437
                this.set_interval_timeouts();
438
            } else {
439
                this.focused = false;
440
                this.set_interval_timeouts();
441
            }
442
        },
443

    
444
        set_interval_timeouts: function(time) {
445
            _.each([this._networks, this._vms], _.bind(function(fetcher){
446
                if (!fetcher) { return };
447
                if (this.focused) {
448
                    fetcher.interval = fetcher.normal_interval;
449
                    fetcher.stop(false).start(true);
450
                } else {
451
                    fetcher.interval = fetcher.maximum_interval;
452
                    fetcher.stop(false).start(false);
453
                }
454

    
455
            }, this));
456
        },
457
        
458
        vms_handlers_registered: false,
459

    
460
        // register event handlers
461
        // 
462
        // vms storage events to identify if vms list 
463
        // is empty and display empty view if user viewing
464
        // a machine view
465
        //
466
        // api/ui error event handlers
467
        init_handlers: function() {
468
            // vm handlers
469
            storage.vms.bind("remove", _.bind(this.check_empty, this));
470
            storage.vms.bind("add", _.bind(this.check_empty, this));
471
            storage.vms.bind("change:status", _.bind(this.check_empty, this));
472
            storage.vms.bind("reset", _.bind(this.check_empty, this));
473
            
474
            // api calls handlers
475
            synnefo.api.bind("error", _.bind(this.handle_api_error, this));
476
            synnefo.api.bind("change:error_state", _.bind(this.handle_api_error_state, this));
477
            synnefo.ui.bind("error", _.bind(this.handle_ui_error, this));
478
        },
479
        
480
        handle_api_error_state: function(state) {
481
            if (snf.api.error_state === snf.api.STATES.ERROR) {
482
                this.stop_intervals();
483
            } else {
484
                if (this.intervals_stopped) {
485
                    this.update_intervals();
486
                }
487
            }
488
        },
489
        
490
        handle_api_error: function(args) {
491
            if (arguments.length == 1) { arguments = _.toArray(arguments[0])};
492

    
493
            if (!_.last(arguments).display) {
494
                return;
495
            }
496

    
497
            this.error_state = true;
498
            
499
            var xhr = arguments[0];
500
            var args = util.parse_api_error.apply(util, arguments);
501
            
502
            // force logout if UNAUTHORIZED request arrives
503
            if (args.code == 401) { snf.ui.logout(); return };
504

    
505
            var error_entry = [args.ns, args.code, args.message, args.type, args.details, args];
506
            this.error_view.show_error.apply(this.error_view, error_entry);
507
        },
508

    
509
        handle_ui_error: function(data) {
510
            var msg = data.msg, code = data.code, err_obj = data.error;
511
            error = msg + "<br /><br />" + snf.util.stacktrace().replace("at", "<br /><br />at");
512
            params = { title: "UI error", extra_details: data.extra };
513
            params.allow_close = data.extra.allow_close === undefined ? true : data.extra.allow_close;
514
            this.error_view.show_error("UI", -1, msg, "JS Exception", error, params);
515
        },
516

    
517
        init_overlays: function() {
518
            this.create_vm_view = new views.CreateVMView();
519
            this.api_info_view = new views.ApiInfoView();
520
            //this.notice_view = new views.NoticeView();
521
        },
522
        
523
        show_loading_view: function() {
524
            $("#container #content").hide();
525
            $("#loading-view").show();
526
        },
527

    
528
        hide_loading_view: function() {
529
            $("#container #content").show();
530
            $("#loading-view").hide();
531
            $(".css-panes").show();
532
        },
533
        
534
        items_to_load: 4,
535
        completed_items: 0,
536
        check_status: function(loaded) {
537
            this.completed_items++;
538
            // images, flavors loaded
539
            if (this.completed_items == 2) {
540
                this.load_nets_and_vms();
541
            }
542

    
543
            if (this.completed_items == this.items_to_load) {
544
                this.update_status("Rendering layout...");
545
                var self = this;
546
                window.setTimeout(function(){
547
                    self.after_load();
548
                }, 10)
549
            }
550
        },
551

    
552
        load_nets_and_vms: function() {
553
            var self = this;
554
            this.update_status("Loading vms...");
555
            storage.vms.fetch({refresh:true, update:false, success: function(){
556
                self.update_status("VMS Loaded.");
557
                self.check_status();
558
            }});
559

    
560
            this.update_status("Loading networks...");
561
            storage.networks.fetch({refresh:true, update:false, success: function(){
562
                self.update_status("Networks loaded.");
563
                self.check_status();
564
            }});
565
        },  
566

    
567
        init_intervals: function() {
568
            var fetcher_params = [snf.config.update_interval, 
569
                                  snf.config.update_interval_increase || 500,
570
                                  snf.config.fast_interval || snf.config.update_interval/2, 
571
                                  snf.config.update_interval_increase_after_calls || 4,
572
                                  snf.config.update_interval_max || 20000,
573
                                  true, 
574
                                  {is_recurrent: true}]
575
            
576
            this._networks = storage.networks.get_fetcher.apply(storage.networks, _.clone(fetcher_params));
577
            this._vms = storage.vms.get_fetcher.apply(storage.vms, _.clone(fetcher_params));
578
        },
579

    
580
        stop_intervals: function() {
581
            if (this._networks) { this._networks.stop(); }
582
            if (this._vms) { this._vms.stop(); }
583
            this.intervals_stopped = true;
584
        },
585

    
586
        update_intervals: function() {
587
            if (this._networks) {
588
                this._networks.stop();
589
                this._networks.start();
590
            } else {
591
                this.init_intervals();
592
            }
593

    
594
            if (this._vms) {
595
                this._vms.stop();
596
                this._vms.start();
597
            } else {
598
                this.init_intervals();
599
            }
600

    
601
            this.intervals_stopped = false;
602
        },
603

    
604
        after_load: function() {
605
            var self = this;
606
            this.update_status("Setting vms update interval...");
607
            this.init_intervals();
608
            this.update_intervals();
609
            this.update_status("Showing initial view...");
610
            
611
            // bypass update_hidden_views in initial view
612
            // rendering to force all views to get render
613
            // on their creation
614
            var uhv = snf.config.update_hidden_views;
615
            snf.config.update_hidden_views = true;
616
            this.initialize_views();
617
            snf.config.update_hidden_views = uhv;
618

    
619
            window.setTimeout(function() {
620
                self.update_status("Initializing overlays...");
621
                self.load_initialize_overlays();
622
            }, 20);
623
        },
624

    
625
        load_initialize_overlays: function() {
626
            this.init_overlays();
627
            // display initial view
628
            this.loaded = true;
629
            
630
            // application start point
631
            this.show_initial_view();
632

    
633
            this.check_empty();
634
        },
635

    
636
        load: function() {
637
            if (synnefo.config.use_glance) {
638
                synnefo.glance.register();
639
            }
640
            this.error_view = new views.ErrorView();
641
            this.feedback_view = new views.FeedbackView();
642
            this.invitations_view = new views.InvitationsView();
643
            this.public_keys_view = new views.PublicKeysOverlay();
644
            
645
            if (synnefo.config.use_glance) {
646
                this.custom_images_view = new views.CustomImagesOverlay();
647
            }
648

    
649
            var self = this;
650
            // initialize overlay views
651
            
652
            // display loading message
653
            this.show_loading_view();
654
            // sync load initial data
655
            this.update_status("Loading images...");
656
            storage.images.fetch({refresh:true, update:false, success: function(){
657
                self.check_status()
658
            }});
659
            this.update_status("Loading flavors...");
660
            storage.flavors.fetch({refresh:true, update:false, success:function(){
661
                self.check_status()
662
            }});
663
        },
664

    
665
        update_status: function(msg) {
666
            this.log.debug(msg)
667
            this.status = msg;
668
            $("#loading-view .info").removeClass("hidden")
669
            $("#loading-view .info").text(this.status);
670
        },
671

    
672
        initialize_views: function() {
673
            this.select_view = new views.SelectView(this, this.router);
674
            this.empty_view = new views.EmptyView();
675
            this.metadata_view = new views.MetadataView();
676
            this.multiple_actions_view = new views.MultipleActionsView();
677
            
678
            this.add_view("icon");
679
            this.add_view("list");
680
            this.add_view("single");
681
            this.add_view("networks");
682

    
683
            this.init_menu();
684
        },
685

    
686
        init_menu: function() {
687
            $(".usermenu .invitations").click(_.bind(function(e){
688
                e.preventDefault();
689
                this.invitations_view.show();
690
            }, this));
691
            $(".usermenu .feedback").click(_.bind(function(e){
692
                e.preventDefault();
693
                this.feedback_view.show();
694
            }, this));
695
            $(".usermenu .public_keys").click(_.bind(function(e){
696
                e.preventDefault();
697
                this.public_keys_view.show();
698
            }, this));
699

    
700
            if (snf.glance) {
701
                $(".usermenu .custom_images").click(_.bind(function(e){
702
                    e.preventDefault();
703
                    this.custom_images_view.show();
704
                }, this));
705
            } else {
706
                $(".usermenu .custom_images").hide();
707
            }
708
        },
709
        
710
        // initial view based on user cookie
711
        show_initial_view: function() {
712
          this.set_vm_view_handlers();
713
          this.hide_loading_view();
714
          
715
          bb.history.start();
716

    
717
          this.trigger("initial");
718
        },
719

    
720
        show_vm_details: function(vm) {
721
            this.router.vm_details_view(vm.id);
722
        },
723

    
724
        set_vm_view_handlers: function() {
725
            var self = this;
726
            $("#createcontainer #create").click(function(e){
727
                e.preventDefault();
728
                self.router.vm_create_view();
729
            })
730
        },
731

    
732
        check_empty: function() {
733
            if (!this.loaded) { return }
734
            if (storage.vms.length == 0) {
735
                this.show_view("machines");
736
                this.router.show_welcome();
737
                this.empty_hidden = false;
738
            } else {
739
                this.hide_empty();
740
            }
741
        },
742

    
743
        show_empty: function() {
744
            if (!this.empty_hidden) { return };
745
            $("#machines-pane-top").addClass("empty");
746

    
747
            this.$(".panes").hide();
748
            this.$("#machines-pane").show();
749

    
750
            this.hide_views([]);
751
            this.empty_hidden = false;
752
            this.empty_view.show();
753
            this.select_view.update_layout();
754
            this.empty_hidden = false;
755
        },
756

    
757
        hide_empty: function() {
758
            if (this.empty_hidden) { return };
759
            $("#machines-pane-top").removeClass("empty");
760

    
761
            this.empty_view.hide(true);
762
            this.router.vms_index();
763
            this.empty_hidden = true;
764
            this.select_view.update_layout();
765
        },
766
        
767
        get_title: function(view_id) {
768
            var view_id = view_id || this.current_view_id;
769
            return this.views_titles[view_id];
770
        },
771

    
772
        // return class object for the given view or false if
773
        // the view is not registered
774
        get_class_for_view: function (view_id) {
775
            if (!this.views_classes[view_id]) {
776
                return false;
777
            }
778
            return this.views_classes[view_id];
779
        },
780

    
781
        view: function(view_id) {
782
            return this.views[view_id];
783
        },
784

    
785
        add_view: function(view_id) {
786
            if (!this.views[view_id]) {
787
                var cls = this.get_class_for_view(view_id);
788
                if (this.skip_errors) {
789
                    this.views[view_id] = new cls();
790
                    $(this.views[view_id]).bind("resize", _.bind(function() {
791
                        window.positionFooter();
792
                        this.multiple_actions_view.fix_position();
793
                    }, this));
794
                } else {
795
                    // catch ui errors
796
                    try {
797
                        this.views[view_id] = new cls();
798
                        $(this.views[view_id]).bind("resize", _.bind(function() {
799
                            window.positionFooter();
800
                            this.multiple_actions_view.fix_position();
801
                        }, this));
802
                    } catch (err) {snf.ui.trigger_error(-1, "Cannot add view", err)}
803
                }
804
            } else {
805
            }
806

    
807
            if (this.views[view_id].vms_view) {
808
                this.views[view_id].metadata_view = this.metadata_view;
809
            }
810
            return this.views[view_id];
811
        },
812
            
813
        hide_views: function(skip) {
814
            _.each(this.views, function(view) {
815
                if (skip.indexOf(view) === -1) {
816
                    $(view.el).hide();
817
                }
818
            }, this)
819
        },
820
        
821
        get: function(view_id) {
822
            return this.views[view_id];
823
        },
824
        
825
        session_view: function() {
826
            if (this.pane_view_from_session() > 0) {
827
                return this.views_pane_indexes[this.pane_view_from_session()];
828
            } else {
829
                return this.views_indexes[this.machine_view_from_session()];
830
            }
831
        },
832

    
833
        pane_view_from_session: function() {
834
            return $.cookie("pane_view") || 0;
835
        },
836

    
837
        machine_view_from_session: function() {
838
            return $.cookie("machine_view") || 0;
839
        },
840

    
841
        update_session: function() {
842
            $.cookie("pane_view", this.pane_ids[this.current_view_id]);
843
            if (this.current_view.vms_view) {
844
                $.cookie("machine_view", this.views_ids[this.current_view_id]);
845
            }
846
        },
847

    
848
        identify_view: function(view_id) {
849
            // machines view_id is an alias to
850
            // one of the 3 machines view
851
            // identify which one (if no cookie set defaults to icon)
852
            if (view_id == "machines") {
853
                var index = this.machine_view_from_session();
854
                view_id = this.views_indexes[index];
855
            }
856
            return view_id;
857
        },
858
        
859
        // switch to current view pane
860
        // if differs from the visible one
861
        show_view_pane: function() {
862
            if (this.current_view.pane != this.current_pane) {
863
                $(this.current_view.pane).show();
864
                $(this.current_pane).hide();
865
                this.current_pane = this.current_view.pane;
866
            }
867
        },
868
        
869
        show_view: function(view_id) {
870
            //var d = new Date;
871
            var ret = this._show_view(view_id);
872
            //console.log((new Date)-d)
873
            return ret;
874
        },
875

    
876
        _show_view: function(view_id) {
877
                // same view, visible
878
                // get out of here asap
879
                if (this.current_view && 
880
                    this.current_view.id == view_id && 
881
                    this.current_view.visible()) {
882
                    return;
883
                }
884
                
885
                // choose proper view_id
886
                view_id = this.identify_view(view_id);
887

    
888
                // add/create view and update current view
889
                var view = this.add_view(view_id);
890
                
891
                // set current view
892
                this.current_view = view;
893
                this.current_view_id = view_id;
894

    
895
                // hide all other views
896
                this.hide_views([this.current_view]);
897
                
898
                // FIXME: depricated
899
                $(".large-spinner").remove();
900

    
901
                storage.vms.reset_pending_actions();
902
                storage.networks.reset_pending_actions();
903
                storage.vms.stop_stats_update();
904

    
905
                // show current view
906
                this.show_view_pane();
907
                view.show();
908
                
909
                // update menus
910
                if (this.select_view) {
911
                    this.select_view.update_layout();
912
                }
913
                this.current_view.__update_layout();
914

    
915
                // update cookies
916
                this.update_session();
917
                
918
                // machines view subnav
919
                if (this.current_view.vms_view) {
920
                    $("#machines-pane").show();
921
                } else {
922
                    $("#machines-pane").hide();
923
                }
924
                
925
                // fix footer position
926
                // TODO: move footer handlers in
927
                // main view (from home.html)
928
                if (window.positionFooter) {
929
                    window.positionFooter();
930
                }
931

    
932
                // trigger view change event
933
                this.trigger("view:change", this.current_view.view_id);
934
                this.select_view.title.text(this.get_title());
935
                $(window).trigger("view:change");
936
                return view;
937
        },
938

    
939
        reset_vm_actions: function() {
940
        
941
        },
942
        
943
        // identify current view
944
        // based on views element visibility
945
        current_view_id: function() {
946
            var found = false;
947
            _.each(this.views, function(key, value) {
948
                if (value.visible()) {
949
                    found = value;
950
                }
951
            })
952
            return found;
953
        }
954

    
955
    });
956

    
957
    snf.ui.main = new views.MainView();
958
    
959
    snf.ui.logout = function() {
960
        $.cookie("X-Auth-Token", null);
961
        if (snf.config.logout_url !== undefined)
962
        {
963
            window.location = snf.config.logout_url;
964
        } else {
965
            window.location.reload();
966
        }
967
    }
968

    
969
    snf.ui.init = function() {
970
        if (snf.config.handle_window_exceptions) {
971
            window.onerror = function(msg, file, line) {
972
                snf.ui.trigger_error("CRITICAL", msg, {}, { file:file + ":" + line, allow_close: false });
973
            };
974
        }
975
        snf.ui.main.load();
976
    }
977

    
978
})(this);