Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / static / snf / js / ui / web / ui_main_view.js @ 3a7e5b0f

History | View | Annotate | Download (33.5 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 = snf.user.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 = snf.user.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
        },
475
        
476
        handle_api_error_state: function(state) {
477
            if (snf.api.error_state === snf.api.STATES.ERROR) {
478
                this.stop_intervals();
479
            } else {
480
                if (this.intervals_stopped) {
481
                    this.update_intervals();
482
                }
483
            }
484
        },
485
        
486
        handle_api_error: function(args) {
487
            if (arguments.length == 1) { arguments = _.toArray(arguments[0])};
488

    
489
            if (!_.last(arguments).display) {
490
                return;
491
            }
492

    
493
            this.error_state = true;
494
            
495
            var xhr = arguments[0];
496
            var args = util.parse_api_error.apply(util, arguments);
497
            
498
            // force logout if UNAUTHORIZED request arrives
499
            if (args.code == 401) { snf.auth_client.redirect_to_login(); return };
500

    
501
            var error_entry = [args.ns, args.code, args.message, args.type, args.details, args];
502
            this.error_view.show_error.apply(this.error_view, error_entry);
503
        },
504

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

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

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

    
539
            if (this.completed_items == this.items_to_load) {
540
                this.update_status("Rendering layout...");
541
                var self = this;
542
                window.setTimeout(function(){
543
                    self.after_load();
544
                }, 10)
545
            }
546
        },
547

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

    
556
            this.update_status("Loading networks...");
557
            storage.networks.fetch({refresh:true, update:false, success: function(){
558
                self.update_status("Networks loaded.");
559
                self.check_status();
560
            }});
561
        },  
562

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

    
576
        stop_intervals: function() {
577
            if (this._networks) { this._networks.stop(); }
578
            if (this._vms) { this._vms.stop(); }
579
            this.intervals_stopped = true;
580
        },
581

    
582
        update_intervals: function() {
583
            if (this._networks) {
584
                this._networks.stop();
585
                this._networks.start();
586
            } else {
587
                this.init_intervals();
588
            }
589

    
590
            if (this._vms) {
591
                this._vms.stop();
592
                this._vms.start();
593
            } else {
594
                this.init_intervals();
595
            }
596

    
597
            this.intervals_stopped = false;
598
        },
599

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

    
615
            window.setTimeout(function() {
616
                self.update_status("Initializing overlays...");
617
                self.load_initialize_overlays();
618
            }, 20);
619
        },
620

    
621
        load_initialize_overlays: function() {
622
            this.init_overlays();
623
            // display initial view
624
            this.loaded = true;
625
            
626
            // application start point
627
            this.show_initial_view();
628

    
629
            this.check_empty();
630
        },
631

    
632
        load: function() {
633
            if (synnefo.config.use_glance) {
634
                synnefo.glance.register();
635
            }
636
            this.error_view = new views.ErrorView();
637
            // api request error handling
638
            synnefo.api.bind("error", _.bind(this.handle_api_error, this));
639
            synnefo.api.bind("change:error_state", _.bind(this.handle_api_error_state, this));
640
            synnefo.ui.bind("error", _.bind(this.handle_ui_error, this));
641

    
642
            this.feedback_view = new views.FeedbackView();
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 .feedback").click(_.bind(function(e){
688
                e.preventDefault();
689
                this.feedback_view.show();
690
            }, this));
691
            $(".usermenu .public_keys").click(_.bind(function(e){
692
                e.preventDefault();
693
                this.public_keys_view.show();
694
            }, this));
695

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

    
713
          this.trigger("ready");
714
        },
715

    
716
        show_vm_details: function(vm) {
717
            this.router.vm_details_view(vm.id);
718
        },
719

    
720
        set_vm_view_handlers: function() {
721
            var self = this;
722
            $("#createcontainer #create").click(function(e){
723
                e.preventDefault();
724
                self.router.vm_create_view();
725
            })
726
        },
727

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

    
739
        show_empty: function() {
740
            if (!this.empty_hidden) { return };
741
            $("#machines-pane-top").addClass("empty");
742

    
743
            this.$(".panes").hide();
744
            this.$("#machines-pane").show();
745

    
746
            this.hide_views([]);
747
            this.empty_hidden = false;
748
            this.empty_view.show();
749
            this.select_view.update_layout();
750
            this.empty_hidden = false;
751
        },
752

    
753
        hide_empty: function() {
754
            if (this.empty_hidden) { return };
755
            $("#machines-pane-top").removeClass("empty");
756

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

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

    
777
        view: function(view_id) {
778
            return this.views[view_id];
779
        },
780

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

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

    
829
        pane_view_from_session: function() {
830
            return $.cookie("pane_view") || 0;
831
        },
832

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

    
837
        update_session: function() {
838
            $.cookie("pane_view", this.pane_ids[this.current_view_id]);
839
            if (this.current_view.vms_view) {
840
                $.cookie("machine_view", this.views_ids[this.current_view_id]);
841
            }
842
        },
843

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

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

    
884
                // add/create view and update current view
885
                var view = this.add_view(view_id);
886
                
887
                // set current view
888
                this.current_view = view;
889
                this.current_view_id = view_id;
890

    
891
                // hide all other views
892
                this.hide_views([this.current_view]);
893
                
894
                // FIXME: depricated
895
                $(".large-spinner").remove();
896

    
897
                storage.vms.reset_pending_actions();
898
                storage.networks.reset_pending_actions();
899
                storage.vms.stop_stats_update();
900

    
901
                // show current view
902
                this.show_view_pane();
903
                view.show();
904
                
905
                // update menus
906
                if (this.select_view) {
907
                    this.select_view.update_layout();
908
                }
909
                this.current_view.__update_layout();
910

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

    
928
                // trigger view change event
929
                this.trigger("view:change", this.current_view.view_id);
930
                this.select_view.title.text(this.get_title());
931
                $(window).trigger("view:change");
932
                return view;
933
        },
934

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

    
951
    });
952

    
953
    snf.ui.main = new views.MainView();
954
    
955
    snf.ui.init = function() {
956
        if (snf.config.handle_window_exceptions) {
957
            window.onerror = function(msg, file, line) {
958
                snf.ui.trigger_error("CRITICAL", msg, {}, { file:file + ":" + line, allow_close: false });
959
            };
960
        }
961
        snf.ui.main.load();
962
    }
963

    
964
})(this);