Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / ui / static / snf / js / ui / web / ui_main_view.js @ 1fc7640d

History | View | Annotate | Download (38 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
    // generic details overlay view.
53
    views.DetailsView = views.Overlay.extend({
54
        view_id: "details_view",
55
        
56
        content_selector: "#details-overlay",
57
        css_class: 'overlay-api-info overlay-info',
58
        overlay_id: "overlay-details",
59

    
60
        subtitle: "",
61
        title: "Details",
62
        
63
        show: function(title, msg, content) {
64
            this.title = title;
65
            this.msg = msg;
66
            this.content = content;
67
            views.DetailsView.__super__.show.apply(this);
68
        },
69

    
70
        beforeOpen: function() {
71
            this.set_title(this.title);
72
            if (!this.msg) { 
73
                this.$(".description.intro").hide() 
74
            } else {
75
                this.$(".description.intro").html(this.msg).show();
76
            }
77

    
78
            if (!this.content) { 
79
                this.$(".description.subinfo").hide() 
80
            } else {
81
                this.$(".description.subinfo").html(this.content).show(); 
82
            };
83
        }
84

    
85
    });
86

    
87
    views.SuspendedVMView = views.FeedbackView.extend({
88
        view_id: "suspended_info_view",
89
        
90
        css_class: 'overlay-api-info overlay-error non-critical',
91
        overlay_id: "overlay-api-info",
92

    
93
        subtitle: "",
94
        title: "VM Suspended",
95

    
96
        beforeOpen: function() {
97
            views.SuspendedVMView.__super__.beforeOpen.apply(this);
98
            $(this.$(".description p")[0]).html($("#suspended-vm-overlay .description").html())
99
        },
100

    
101
        show: function(vm, data, collect_data, extra_data, cb) {
102
            this.vm = vm;
103
            data = "Suspended VM Details";
104
            data += "\n====================";
105
            data += "\nID: " + vm.id;
106
            data += "\nName: " + vm.get('name');
107
            data += "\nPublic IP: " + vm.get_public_nic().get('ipv4');
108
            data += "\n\n";
109
            views.SuspendedVMView.__super__.show.call(this, data, collect_data, extra_data, cb);
110
        }
111

    
112
    });
113

    
114
    views.ApiInfoView = views.Overlay.extend({
115
        view_id: "api_info_view",
116
        
117
        content_selector: "#api-info-overlay",
118
        css_class: 'overlay-api-info overlay-info',
119
        overlay_id: "overlay-api-info",
120

    
121
        subtitle: "",
122
        title: "API Access",
123

    
124
        beforeOpen: function() {
125
            var cont = this.$(".copy-content p");
126
            var token = snf.user.get_token();
127

    
128
            cont.html("");
129
            cont.text(token);
130
            
131
            this.cont = cont;
132
            this.token = token;
133
            try { delete this.clip; } catch (err) {};
134
        },
135

    
136
        onOpen: function() {
137
            views.ApiInfoView.__super__.onOpen(this, arguments);
138
            this.clip = new snf.util.ClipHelper(this.cont.parent(), this.token);
139
        },
140

    
141
        onClose: function() {
142
            var cont = this.$(".copy-content p");
143
            var token = snf.user.token;
144
            cont.html("");
145
        }
146
    });
147

    
148
    // TODO: implement me
149
    views.NoticeView = views.Overlay.extend({});
150

    
151
    views.MultipleActionsView = views.View.extend({
152
        view_id: "multiple_actions",
153

    
154
        _actions: {},
155
        el: '#multiple_actions_container',
156
        
157
        initialize: function() {
158
            this.actions = {};
159
            this.ns_config = {};
160

    
161
            views.MultipleActionsView.__super__.initialize.call(this);
162

    
163
            this.ns_tpl = this.$(".confirm_multiple_actions-template").clone()
164

    
165
            this.init_handlers();
166
            this.update_layout();
167
            
168
            // for heavy resize/scroll window events
169
            // do it `like a boss` 
170
            this.fix_position = _.throttle(this.fix_position, 100);
171
            this.update_layout = _.throttle(this.update_layout, 100);
172
            this.show_limit = 1;
173

    
174
            this.init_ns("vms", {
175
                msg_tpl:"Your actions will affect 1 machine",
176
                msg_tpl_plural:"Your actions will affect {0} machines",
177
                actions_msg: {confirm: "Confirm all", cancel: "Cancel all"},
178
                limit: 1,
179
                cancel_all: function() { snf.storage.vms.reset_pending_actions(); },
180
                do_all: function() { snf.storage.vms.do_all_pending_actions(); }
181
            });
182
            
183
            this.init_ns("nets", {
184
                msg_tpl:"Your actions will affect 1 private network",
185
                msg_tpl_plural:"Your actions will affect {0} private networks",
186
                actions_msg: {confirm: "Confirm all", cancel: "Cancel all"},
187
                limit: 1,
188
                cancel_all: function() { snf.storage.networks.reset_pending_actions(); },
189
                do_all: function() { snf.storage.networks.do_all_pending_actions(); }
190
            });
191

    
192
            this.init_ns("reboots", {
193
                msg_tpl:"1 machine needs to be rebooted for changes to apply.",
194
                msg_tpl_plural:"{0} machines needs to be rebooted for changes to apply.",
195
                actions_msg: {confirm: "Reboot all", cancel: "Cancel all"},
196
                limit: 0,
197
                cancel_all: function() { snf.storage.vms.reset_reboot_required(); },
198
                do_all: function() { snf.storage.vms.do_all_reboots(); }
199
            });
200
        },
201
        
202
        init_ns: function(ns, params) {
203
            this.actions[ns] = {};
204
            var nsconf = this.ns_config[ns] = params || {};
205
            nsconf.cont = $(this.$("#conirm_multiple_cont_template").clone());
206
            nsconf.cont.attr("id", "confirm_multiple_cont_" + ns);
207
            $(this.el).find(".ns-confirms-cont").append(nsconf.cont).addClass(ns);
208
            $(this.el).find(".ns-confirms-cont").append(nsconf.cont).addClass("confirm-ns");
209
            nsconf.cont.find(".msg button.yes").text(
210
                nsconf.actions_msg.confirm).click(_.bind(this.do_all, this, ns));
211
            nsconf.cont.find(".msg button.no").text(
212
                nsconf.actions_msg.cancel).click(_.bind(this.cancel_all, this, ns));
213
        },
214

    
215
        do_all: function(ns) {
216
            this.ns_config[ns].do_all();
217
        },
218

    
219
        cancel_all: function(ns) {
220
            this.ns_config[ns].cancel_all();
221
        },
222

    
223
        init_handlers: function() {
224
            var self = this;
225

    
226
            $(window).resize(_.bind(function(){
227
                this.fix_position();
228
            }, this));
229

    
230
            $(window).scroll(_.bind(function(){
231
                this.fix_position();
232
            }, this));
233

    
234
            storage.vms.bind("change:pending_action", 
235
                             _.bind(this.handle_action_add, this, "vms"));
236
            storage.vms.bind("change:reboot_required", 
237
                             _.bind(this.handle_action_add, this, "reboots"));
238
            storage.networks.bind("change:actions", 
239
                                  _.bind(this.handle_action_add, this, "nets"));
240
        },
241

    
242
        handle_action_add: function(type, model, action) {
243
            var actions = this.actions[type];
244
            
245
            // TODO: remove type specific addition code in its own namespace
246
            if (type == "nets") {
247
                if (!action || action.is_empty()) {
248
                    delete actions[model.id];
249
                } else {
250
                    actions[model.id] = {model: model, actions: action.actions};
251
                }
252
            }
253

    
254
            if (type == "vms") {
255
                _.each(actions, function(action) {
256
                    if (action.model.id == model.id) {
257
                        delete actions[action]
258
                    }
259
                });
260

    
261
                var actobject = {};
262
                actobject[action] = [[]];
263
                actions[model.id] = {model: model, actions: actobject};
264
                if (typeof action == "undefined") {
265
                    delete actions[model.id]
266
                }
267
            }
268

    
269
            if (type == "reboots") {
270
                _.each(actions, function(action) {
271
                    if (action.model.id == model.id) {
272
                        delete actions[action]
273
                    }
274
                });
275
                var actobject = {};
276
                actobject['reboot'] = [[]];
277
                actions[model.id] = {model: model, actions: actobject};
278
                if (!action) {
279
                    delete actions[model.id]
280
                }
281
            }
282
            
283
            this.update_layout();
284
        },
285

    
286
        update_actions_content: function(ns) {
287
            var conf = this.ns_config[ns];
288
            conf.cont.find(".details").empty();
289
            conf.cont.find(".msg p").text("");
290
            
291
            var count = 0;
292
            var actionscount = 0;
293
            _.each(this.actions[ns], function(actions, model_id) {
294
                count++;
295
                _.each(actions.actions, function(params, act_name){
296
                    if (params && params.length) {
297
                        actionscount += params.length;
298
                    } else {
299
                        actionscount++;
300
                    }
301
                })
302
                this.total_confirm_actions++;
303
            });
304
            
305
            var limit = conf.limit;
306
            if (ui.main.current_view.view_id == "vm_list") {
307
                limit = 0;
308
            }
309

    
310
            if (actionscount > limit) {
311
                conf.cont.show();
312
                this.confirm_ns_open++;
313
            } else {
314
                conf.cont.hide();
315
            }
316
            
317
            var msg = count > 1 ? conf.msg_tpl_plural : conf.msg_tpl;
318
            conf.cont.find(".msg p").text(msg.format(count));
319

    
320
            return conf.cont;
321
        },
322

    
323
        fix_position: function() {
324
            $('.confirm_multiple').removeClass('fixed');
325
            if (($(this.el).offset().top +$(this.el).height())> ($(window).scrollTop() + $(window).height())) {
326
                $('.confirm_multiple').addClass('fixed');
327
            }
328
        },
329
        
330
        update_layout: function() {
331
            this.confirm_ns_open = 0;
332
            this.total_confirm_actions = 0;
333

    
334
            $(this.el).show();
335
            $(this.el).find("#conirm_multiple_cont_template").hide();
336
            $(this.el).find(".confirm-ns").show();
337
            
338
            _.each(this.ns_config, _.bind(function(params, key) {
339
                this.update_actions_content(key);
340
            }, this));
341

    
342
            if (this.confirm_ns_open > 0) {
343
                $(this.el).show();
344
                this.$(".confirm-all-cont").hide();
345
                this.$(".ns-confirms-cont").show();
346
            } else {
347
                $(this.el).hide();
348
                this.$(".confirm-all-cont").hide();
349
                this.$(".ns-confirms-cont").hide();
350
            }
351

    
352
            $(window).trigger("resize");
353
        }
354
    })
355
    
356
    // menu wrapper view
357
    views.SelectView = views.View.extend({
358
        
359
        initialize: function(view, router) {
360
            this.parent = view;
361
            this.router = router;
362
            this.pane_view_selector = $(".css-tabs");
363
            this.machine_view_selector = $("#view-select");
364
            this.el = $(".css-tabs");
365
            this.title = $(".tab-name");
366

    
367
            this.set_handlers();
368
            this.update_layout();
369

    
370
            views.SelectView.__super__.initialize.apply(this, arguments);
371
        },
372
        
373
        clear_active: function() {
374
            this.pane_view_selector.find("a").removeClass("active");
375
            this.machine_view_selector.find("a").removeClass("activelink");
376
        },
377
        
378
        // intercept menu links
379
        set_handlers: function() {
380
            var self = this;
381
            this.pane_view_selector.find("a").hover(function(){
382
                // FIXME: title from href ? omg
383
                self.title.text($(this).attr("href"));
384
            }, function(){
385
                self.title.text(self.parent.get_title());
386
            });
387

    
388
            this.pane_view_selector.find("a#machines_view_link").click(_.bind(function(ev){
389
                ev.preventDefault();
390
                this.router.vms_index();
391
            }, this))
392
            this.pane_view_selector.find("a#networks_view_link").click(_.bind(function(ev){
393
                ev.preventDefault();
394
                this.router.networks_view();
395
            }, this))
396
            this.pane_view_selector.find("a#disks_view_link").click(_.bind(function(ev){
397
                ev.preventDefault();
398
                this.router.disks_view();
399
            }, this))
400
            
401
            this.machine_view_selector.find("a#machines_view_icon_link").click(_.bind(function(ev){
402
                ev.preventDefault();
403
                var d = $.now();
404
                this.router.vms_icon_view();
405
            }, this))
406
            this.machine_view_selector.find("a#machines_view_list_link").click(_.bind(function(ev){
407
                ev.preventDefault();
408
                this.router.vms_list_view();
409
            }, this))
410
            this.machine_view_selector.find("a#machines_view_single_link").click(_.bind(function(ev){
411
                ev.preventDefault();
412
                this.router.vms_single_view();
413
            }, this))
414
        },
415

    
416
        update_layout: function() {
417
            this.clear_active();
418

    
419
            var pane_index = this.parent.pane_ids[this.parent.current_view_id];
420
            $(this.pane_view_selector.find("a")).removeClass("active");
421
            $(this.pane_view_selector.find("a").get(pane_index)).addClass("active");
422
            
423
            if (this.parent.current_view && this.parent.current_view.vms_view) {
424

    
425
                if (storage.vms.length > 0) {
426
                    this.machine_view_selector.show();
427
                    var machine_index = this.parent.views_ids[this.parent.current_view_id];
428
                    $(this.machine_view_selector.find("a").get(machine_index)).addClass("activelink");
429
                } else {
430
                    this.machine_view_selector.hide();
431
                }
432
            } else {
433
                this.machine_view_selector.hide();
434
            }
435

    
436
        }
437
    });
438

    
439
    views.MainView = views.View.extend({
440
        el: 'body',
441
        view_id: 'main',
442
        
443
        // FIXME: titles belong to SelectView
444
        views_titles: {
445
            'icon': 'machines', 'single': 'machines', 
446
            'list': 'machines', 'networks': 'networks',
447
            'disks': 'disks'
448
        },
449

    
450
        // indexes registry
451
        views_indexes: {0: 'icon', 2:'single', 1: 'list', 3:'networks'},
452
        views_pane_indexes: {0:'single', 1:'networks', 2:'disks'},
453

    
454
        // views classes registry
455
        views_classes: {'icon': views.IconView, 'single': views.SingleView, 
456
            'list': views.ListView, 'networks': views.NetworksView, 'disks': views.DisksView},
457

    
458
        // view ids
459
        views_ids: {'icon':0, 'single':2, 'list':1, 'networks':3, 'disks':4},
460

    
461
        // on which pane id each view exists
462
        // machine views (icon,single,list) are all on first pane
463
        pane_ids: {'icon':0, 'single':0, 'list':0, 'networks':1, 'disks':2},
464
    
465
        initialize: function(show_view) {
466
            if (!show_view) { show_view = 'icon' };
467
            
468
            this.router = snf.router;
469
            this.empty_hidden = true;
470
            // fallback to browser error reporting (true for debug)
471
            this.skip_errors = true
472

    
473
            // reset views
474
            this.views = {};
475

    
476
            this.el = $("#app");
477
            // reset main view status
478
            this._loaded = false;
479
            this.status = "Initializing...";
480

    
481
            // initialize handlers
482
            this.init_handlers();
483

    
484
            // identify initial view from user cookies
485
            // this view will be visible after loading of
486
            // main view
487
            this.initial_view = this.session_view();
488

    
489
            views.MainView.__super__.initialize.call(this);
490

    
491
            $(window).focus(_.bind(this.handle_window_focus, this, "focus"));
492
            $(window).blur(_.bind(this.handle_window_focus, this, "out"));
493

    
494
            this.focused = true;
495
        },
496

    
497
        handle_window_focus: function(focus) {
498
            if (!snf.config.delay_on_blur) { return };
499

    
500
            if (focus === "focus") {
501
                this.focused = true;
502
                this.set_interval_timeouts();
503
            } else {
504
                this.focused = false;
505
                this.set_interval_timeouts();
506
            }
507
        },
508

    
509
        set_interval_timeouts: function(time) {
510
            _.each([this._networks, this._vms], _.bind(function(fetcher){
511
                if (!fetcher) { return };
512
                if (this.focused) {
513
                    fetcher.interval = fetcher.normal_interval;
514
                    fetcher.stop(false).start(true);
515
                } else {
516
                    fetcher.interval = fetcher.maximum_interval;
517
                    fetcher.stop(false).start(false);
518
                }
519

    
520
            }, this));
521
        },
522
        
523
        vms_handlers_registered: false,
524

    
525
        // register event handlers
526
        // 
527
        // vms storage events to identify if vms list 
528
        // is empty and display empty view if user viewing
529
        // a machine view
530
        //
531
        // api/ui error event handlers
532
        init_handlers: function() {
533
            // vm handlers
534
            storage.vms.bind("remove", _.bind(this.check_empty, this));
535
            storage.vms.bind("add", _.bind(this.check_empty, this));
536
            storage.vms.bind("change:status", _.bind(this.check_empty, this));
537
            storage.vms.bind("reset", _.bind(this.check_empty, this));
538
            storage.quotas.bind("change", _.bind(this.update_create_buttons_status, this));
539
            storage.quotas.bind("add", _.bind(this.update_create_buttons_status, this));
540
            
541
        },
542
        
543
        handle_api_error_state: function(state) {
544
            if (snf.api.error_state === snf.api.STATES.ERROR) {
545
                this.stop_intervals();
546
            } else {
547
                if (this.intervals_stopped) {
548
                    this.update_intervals();
549
                }
550
            }
551
        },
552
        
553
        handle_api_error: function(args) {
554
            if (arguments.length == 1) { arguments = _.toArray(arguments[0])};
555

    
556
            if (!_.last(arguments).display) {
557
                return;
558
            }
559

    
560
            this.error_state = true;
561
            
562
            var xhr = arguments[0];
563
            var args = util.parse_api_error.apply(util, arguments);
564
            
565
            // force logout if UNAUTHORIZED request arrives
566
            if (args.code == 401) { snf.auth_client.redirect_to_login(); return };
567

    
568
            var error_entry = [args.ns, args.code, args.message, args.type, args.details, args];
569
            this.error_view.show_error.apply(this.error_view, error_entry);
570
        },
571

    
572
        handle_ui_error: function(data) {
573
            var msg = data.msg, code = data.code, err_obj = data.error;
574
            error = msg + "<br /><br />" + snf.util.stacktrace().replace("at", "<br /><br />at");
575
            params = { title: "UI error", extra_details: data.extra };
576
            delete data.extra.allow_close;
577
            params.allow_close = data.extra.allow_close === undefined ? true : data.extra.allow_close;
578
            this.error_view.show_error("UI", -1, msg, "JS Exception", error, params);
579
        },
580

    
581
        init_overlays: function() {
582
            this.create_vm_view = new views.CreateVMView();
583
            this.api_info_view = new views.ApiInfoView();
584
            this.details_view = new views.DetailsView();
585
            this.suspended_view = new views.SuspendedVMView();
586
            //this.notice_view = new views.NoticeView();
587
        },
588
        
589
        show_loading_view: function() {
590
            $("#container #content").hide();
591
            $("#loading-view").show();
592
        },
593

    
594
        hide_loading_view: function() {
595
            $("#container #content").show();
596
            $("#loading-view").hide();
597
            $(".css-panes").show();
598
        },
599
        
600
        items_to_load: 6,
601
        completed_items: 0,
602
        check_status: function(loaded) {
603
            this.completed_items++;
604
            // images, flavors loaded
605
            if (this.completed_items == this.items_to_load) {
606
                this.update_status("layout", 1);
607
                var self = this;
608
                window.setTimeout(function(){
609
                    self.after_load();
610
                }, 10)
611
            }
612
        },
613
            
614
        load_missing_images: function(cb) {
615
            synnefo.storage.vms.load_missing_images(cb);
616
        },
617

    
618
        load_nets_and_vms: function() {
619
            var self = this;
620
            this.update_status("vms", 0);
621
            storage.vms.fetch({refresh:true, update:false, success: function(){
622
                self.load_missing_images(function(){
623
                    self.update_status("vms", 1);
624
                    self.update_status("layout", 0);
625
                    self.check_status();
626
                });
627
            }});
628

    
629
            this.update_status("networks", 0);
630
            storage.networks.fetch({refresh:true, update:false, success: function(){
631
                self.update_status("networks", 1);
632
                self.check_status();
633
            }});
634

    
635
        },  
636

    
637
        init_intervals: function() {
638
            var fetcher_params = [snf.config.update_interval, 
639
                                  snf.config.update_interval_increase || 500,
640
                                  snf.config.fast_interval || snf.config.update_interval/2, 
641
                                  snf.config.update_interval_increase_after_calls || 4,
642
                                  snf.config.update_interval_max || 20000,
643
                                  true, 
644
                                  {is_recurrent: true}]
645
            
646
            this._networks = storage.networks.get_fetcher.apply(storage.networks, _.clone(fetcher_params));
647
            this._vms = storage.vms.get_fetcher.apply(storage.vms, _.clone(fetcher_params));
648
            this._quotas = storage.quotas.get_fetcher.apply(storage.quotas, _.clone(fetcher_params));
649
        },
650

    
651
        stop_intervals: function() {
652
            if (this._networks) { this._networks.stop(); }
653
            if (this._vms) { this._vms.stop(); }
654
            if (this._quotas) { this._quotas.stop(); }
655
            this.intervals_stopped = true;
656
        },
657

    
658
        update_intervals: function() {
659
            if (this._networks) {
660
                this._networks.stop();
661
                this._networks.start();
662
            } else {
663
                this.init_intervals();
664
            }
665

    
666
            if (this._vms) {
667
                this._vms.stop();
668
                this._vms.start();
669
            } else {
670
                this.init_intervals();
671
            }
672

    
673
            if (this._quotas) {
674
                this._quotas.stop();
675
                this._quotas.start();
676
            } else {
677
                this.init_intervals();
678
            }
679

    
680
            this.intervals_stopped = false;
681
        },
682

    
683
        after_load: function() {
684
            var self = this;
685
            this.init_intervals();
686
            this.update_intervals();
687
            this.update_status("layout", 0);
688
            
689
            // bypass update_hidden_views in initial view
690
            // rendering to force all views to get render
691
            // on their creation
692
            var uhv = snf.config.update_hidden_views;
693
            snf.config.update_hidden_views = true;
694
            this.initialize_views();
695
            snf.config.update_hidden_views = uhv;
696

    
697
            window.setTimeout(function() {
698
                self.load_initialize_overlays();
699
            }, 20);
700
        },
701

    
702
        load_initialize_overlays: function() {
703
            this.init_overlays();
704
            // display initial view
705
            this.loaded = true;
706
            
707
            // application start point
708

    
709
            this.check_empty();
710
            this.show_initial_view();
711
        },
712

    
713
        load: function() {
714
            if (synnefo.config.use_glance) {
715
                synnefo.glance.register();
716
            }
717
            this.error_view = new views.ErrorView();
718
            this.vm_resize_view = new views.VmResizeView();
719
            // api request error handling
720
            synnefo.api.bind("error", _.bind(this.handle_api_error, this));
721
            synnefo.api.bind("change:error_state", _.bind(this.handle_api_error_state, this));
722
            synnefo.ui.bind("error", _.bind(this.handle_ui_error, this));
723

    
724
            this.feedback_view = new views.FeedbackView();
725
            this.public_keys_view = new views.PublicKeysOverlay();
726
            this.public_ips_view = new views.PublicIPsOverlay();
727
            
728
            if (synnefo.config.use_glance) {
729
                this.custom_images_view = new views.CustomImagesOverlay();
730
            }
731

    
732
            var self = this;
733
            // initialize overlay views
734
            
735
            // display loading message
736
            this.show_loading_view();
737
            // sync load initial data
738
            this.update_status("images", 0);
739
            storage.images.fetch({refresh:true, update:false, success: function(){
740
                self.update_status("images", 1);
741
                self.check_status();
742
                self.load_nets_and_vms();
743
            }});
744
            this.update_status("flavors", 0);
745
            storage.flavors.fetch({refresh:true, update:false, success:function(){
746
                self.update_status("flavors", 1);
747
                self.check_status()
748
            }});
749

    
750
            this.update_status("resources", 0);
751
            storage.resources.fetch({refresh:true, update:false, success: function(){
752
                self.update_status("resources", 1);
753
                self.update_status("quotas", 0);
754
                self.check_status();
755
                storage.quotas.fetch({refresh:true, update:true, success: function() {
756
                  self.update_status("quotas", 1);
757
                  self.update_status("layout", 1);
758
                  self.check_status()
759
                }})
760
            }})
761
        },
762

    
763
        update_status: function(ns, state) {
764
            var el = $("#loading-view .header."+ns);
765
            if (state == 0) {
766
                el.removeClass("off").addClass("on");
767
            }
768
            if (state == 1) {
769
                el.removeClass("on").addClass("done");
770
            }
771
        },
772

    
773
        initialize_views: function() {
774
            this.select_view = new views.SelectView(this, this.router);
775
            this.empty_view = new views.EmptyView();
776
            this.metadata_view = new views.MetadataView();
777
            this.multiple_actions_view = new views.MultipleActionsView();
778
            
779
            this.add_view("icon");
780
            this.add_view("list");
781
            this.add_view("single");
782
            this.add_view("networks");
783
            this.add_view("disks");
784

    
785
            this.init_menu();
786
        },
787

    
788
        init_menu: function() {
789
            $(".usermenu .feedback").click(_.bind(function(e){
790
                e.preventDefault();
791
                this.feedback_view.show();
792
            }, this));
793
            $(".usermenu .public_keys").click(_.bind(function(e){
794
                e.preventDefault();
795
                this.public_keys_view.show();
796
            }, this));
797
            $(".usermenu .public_ips").click(_.bind(function(e){
798
                e.preventDefault();
799
                this.public_ips_view.show();
800
            }, this));
801

    
802
            if (snf.glance) {
803
                $(".usermenu .custom_images").click(_.bind(function(e){
804
                    e.preventDefault();
805
                    this.custom_images_view.show();
806
                }, this));
807
            } else {
808
                $(".usermenu .custom_images").hide();
809
            }
810
        },
811
        
812
        // initial view based on user cookie
813
        show_initial_view: function() {
814
          this.set_vm_view_handlers();
815
          this.hide_loading_view();
816
          bb.history.start();
817
          this.trigger("ready");
818
        },
819

    
820
        show_vm_details: function(vm) {
821
            if (vm) {
822
              this.router.vm_details_view(vm.id);
823
            }
824
        },
825
        
826
        update_create_buttons_status: function() {
827
            var nets = storage.quotas.get('cyclades.network.private');
828
            var vms = storage.quotas.get('cyclades.vm');
829
            
830
            if (!nets || !vms) { return }
831

    
832
            if (!nets.can_consume()) {
833
                $("#networks-pane a.createbutton").addClass("disabled");
834
            } else {
835
                $("#networks-pane a.createbutton").removeClass("disabled");
836
            }
837

    
838
            if (!vms.can_consume()) {
839
                $("#createcontainer #create").addClass("disabled");
840
            } else {
841
                $("#createcontainer #create").removeClass("disabled");
842
            }
843
        },
844

    
845
        set_vm_view_handlers: function() {
846
            var self = this;
847
            $("#createcontainer #create").click(function(e){
848
                e.preventDefault();
849
                if ($(this).hasClass("disabled")) { return }
850
                self.router.vm_create_view();
851
            });
852
        },
853

    
854
        check_empty: function() {
855
            if (!this.loaded) { return }
856
            if (storage.vms.length == 0) {
857
                this.show_view("machines");
858
                this.router.show_welcome();
859
                this.empty_hidden = false;
860
            } else {
861
                this.hide_empty();
862
            }
863
        },
864

    
865
        show_empty: function() {
866
            if (!this.empty_hidden) { return };
867
            $("#machines-pane-top").addClass("empty");
868

    
869
            this.$(".panes").hide();
870
            this.$("#machines-pane").show();
871

    
872
            this.hide_views([]);
873
            this.empty_hidden = false;
874
            this.empty_view.show();
875
            this.select_view.update_layout();
876
            this.empty_hidden = false;
877
        },
878

    
879
        hide_empty: function() {
880
            if (this.empty_hidden) { return };
881
            $("#machines-pane-top").removeClass("empty");
882

    
883
            this.empty_view.hide(true);
884
            this.router.vms_index();
885
            this.empty_hidden = true;
886
            this.select_view.update_layout();
887
        },
888
        
889
        get_title: function(view_id) {
890
            var view_id = view_id || this.current_view_id;
891
            return this.views_titles[view_id];
892
        },
893

    
894
        // return class object for the given view or false if
895
        // the view is not registered
896
        get_class_for_view: function (view_id) {
897
            if (!this.views_classes[view_id]) {
898
                return false;
899
            }
900
            return this.views_classes[view_id];
901
        },
902

    
903
        view: function(view_id) {
904
            return this.views[view_id];
905
        },
906

    
907
        add_view: function(view_id) {
908
            if (!this.views[view_id]) {
909
                var cls = this.get_class_for_view(view_id);
910
                if (this.skip_errors) {
911
                    this.views[view_id] = new cls();
912
                    $(this.views[view_id]).bind("resize", _.bind(function() {
913
                        window.forcePositionFooter();
914
                        this.multiple_actions_view.fix_position();
915
                    }, this));
916
                } else {
917
                    // catch ui errors
918
                    try {
919
                        this.views[view_id] = new cls();
920
                        $(this.views[view_id]).bind("resize", _.bind(function() {
921
                            window.positionFooter();
922
                            this.multiple_actions_view.fix_position();
923
                        }, this));
924
                    } catch (err) {snf.ui.trigger_error(-1, "Cannot add view", err)}
925
                }
926
            } else {
927
            }
928

    
929
            if (this.views[view_id].vms_view) {
930
                this.views[view_id].metadata_view = this.metadata_view;
931
            }
932
            return this.views[view_id];
933
        },
934
            
935
        hide_views: function(skip) {
936
            _.each(this.views, function(view) {
937
                if (skip.indexOf(view) === -1) {
938
                    $(view.el).hide();
939
                }
940
            }, this)
941
        },
942
        
943
        get: function(view_id) {
944
            return this.views[view_id];
945
        },
946
        
947
        session_view: function() {
948
            if (this.pane_view_from_session() > 0) {
949
                return this.views_pane_indexes[this.pane_view_from_session()];
950
            } else {
951
                return this.views_indexes[this.machine_view_from_session()];
952
            }
953
        },
954

    
955
        pane_view_from_session: function() {
956
            return $.cookie("pane_view") || 0;
957
        },
958

    
959
        machine_view_from_session: function() {
960
            return $.cookie("machine_view") || 0;
961
        },
962

    
963
        update_session: function() {
964
            $.cookie("pane_view", this.pane_ids[this.current_view_id]);
965
            if (this.current_view.vms_view) {
966
                $.cookie("machine_view", this.views_ids[this.current_view_id]);
967
            }
968
        },
969

    
970
        identify_view: function(view_id) {
971
            // machines view_id is an alias to
972
            // one of the 3 machines view
973
            // identify which one (if no cookie set defaults to icon)
974
            if (view_id == "machines") {
975
                var index = this.machine_view_from_session();
976
                view_id = this.views_indexes[index];
977
            }
978
            return view_id;
979
        },
980
        
981
        // switch to current view pane
982
        // if differs from the visible one
983
        show_view_pane: function() {
984
            if (this.current_view.pane != this.current_pane) {
985
                $(this.current_view.pane).show();
986
                $(this.current_pane).hide();
987
                this.current_pane = this.current_view.pane;
988
            }
989
        },
990
        
991
        show_view: function(view_id) {
992
            //var d = new Date;
993
            var ret = this._show_view(view_id);
994
            //console.log((new Date)-d)
995
            return ret;
996
        },
997

    
998
        _show_view: function(view_id) {
999
                // same view, visible
1000
                // get out of here asap
1001
                if (this.current_view && 
1002
                    this.current_view.id == view_id && 
1003
                    this.current_view.visible()) {
1004
                    return;
1005
                }
1006
                
1007
                // choose proper view_id
1008
                view_id = this.identify_view(view_id);
1009

    
1010
                // add/create view and update current view
1011
                var view = this.add_view(view_id);
1012
                
1013
                // set current view
1014
                this.current_view = view;
1015
                this.current_view_id = view_id;
1016

    
1017
                // hide all other views
1018
                this.hide_views([this.current_view]);
1019
                
1020
                // FIXME: depricated
1021
                $(".large-spinner").remove();
1022

    
1023
                storage.vms.reset_pending_actions();
1024
                storage.networks.reset_pending_actions();
1025
                storage.vms.stop_stats_update();
1026

    
1027
                // show current view
1028
                this.show_view_pane();
1029
                view.show();
1030
                
1031
                // update menus
1032
                if (this.select_view) {
1033
                    this.select_view.update_layout();
1034
                }
1035
                this.current_view.__update_layout();
1036

    
1037
                // update cookies
1038
                this.update_session();
1039
                
1040
                // machines view subnav
1041
                if (this.current_view.vms_view) {
1042
                    $("#machines-pane").show();
1043
                } else {
1044
                    $("#machines-pane").hide();
1045
                }
1046
                
1047
                // fix footer position
1048
                // TODO: move footer handlers in
1049
                // main view (from home.html)
1050
                if (window.positionFooter) {
1051
                    window.positionFooter();
1052
                }
1053

    
1054
                // trigger view change event
1055
                this.trigger("view:change", this.current_view.view_id);
1056
                this.select_view.title.text(this.get_title());
1057
                $(window).trigger("view:change");
1058
                return view;
1059
        },
1060

    
1061
        reset_vm_actions: function() {
1062
        
1063
        },
1064
        
1065
        // identify current view
1066
        // based on views element visibility
1067
        current_view_id: function() {
1068
            var found = false;
1069
            _.each(this.views, function(key, value) {
1070
                if (value.visible()) {
1071
                    found = value;
1072
                }
1073
            })
1074
            return found;
1075
        }
1076

    
1077
    });
1078

    
1079
    snf.ui.main = new views.MainView();
1080
    
1081
    snf.ui.init = function() {
1082
        if (snf.config.handle_window_exceptions) {
1083
            window.onerror = function(msg, file, line) {
1084
                snf.ui.trigger_error("CRITICAL", msg, {}, { file:file + ":" + line, allow_close: true });
1085
            };
1086
        }
1087
        snf.ui.main.load();
1088
    }
1089

    
1090
})(this);