Statistics
| Branch: | Tag: | Revision:

root / ui / static / snf / js / ui / web / ui_main_view.js @ 35855ff5

History | View | Annotate | Download (26.1 kB)

1
;(function(root){
2

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

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

    
14
    // shortcuts
15
    var bb = root.Backbone;
16
    var util = snf.util;
17
    
18
    // TODO: implement me
19
    views.NoticeView = views.Overlay.extend({});
20

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

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

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

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

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

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

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

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

    
76
        },
77

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
266
        }
267
    });
268

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

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

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

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

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

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

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

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

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

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

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

    
322
            this.focused = true;
323
        },
324

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

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

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

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

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

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

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

    
396
        handle_ui_error: function(error) {
397
            error = error + "<br /><br />" + snf.util.stacktrace().replace("at", "<br /><br />at");
398
            this.error_view.show_error("Application", -1, "Something went wrong", "JS Exception", error);
399
        },
400

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

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

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

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

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

    
454
        stop_intervals: function() {
455
            if (this._networks) { this._networks.stop(); }
456
            if (this._vms) { this._vms.stop(); }
457
            this.intervals_stopped = true;
458
        },
459

    
460
        update_intervals: function() {
461
            if (this._networks) {
462
                this._networks.stop();
463
                this._networks.start();
464
            } else {
465
                this.init_intervals();
466
            }
467

    
468
            if (this._vms) {
469
                this._vms.stop();
470
                this._vms.start();
471
            } else {
472
                this.init_intervals();
473
            }
474

    
475
            this.intervals_stopped = false;
476
        },
477

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

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

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

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

    
531
            this.init_menu();
532
        },
533

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

    
548
        show_vm_details: function(vm) {
549
            snf.ui.main.show_view("single")
550
            snf.ui.main.current_view.show_vm(vm);
551
        },
552

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

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

    
570
        show_empty: function() {
571
            $("#machines-pane-top").addClass("empty");
572

    
573
            this.$(".panes").hide();
574
            this.$("#machines-pane").show();
575

    
576
            this.hide_views([]);
577
            this.empty_view.show();
578
        },
579

    
580
        hide_empty: function() {
581
            $("#machines-pane-top").removeClass("empty");
582

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

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

    
604
        view: function(view_id) {
605
            return this.views[view_id];
606
        },
607

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

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

    
656
        pane_view_from_session: function() {
657
            return $.cookie("pane_view") || 0;
658
        },
659

    
660
        machine_view_from_session: function() {
661
            return $.cookie("machine_view") || 0;
662
        },
663

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

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

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

    
704
            // add/create view and update current view
705
            var view = this.add_view(view_id);
706
            
707
            // set current view
708
            this.current_view = view;
709
            this.current_view_id = view_id;
710

    
711
            // hide all other views
712
            this.hide_views([this.current_view]);
713
            
714
            // FIXME: depricated
715
            $(".large-spinner").remove();
716

    
717
            storage.vms.reset_pending_actions();
718
            storage.vms.stop_stats_update();
719

    
720
            // show current view
721
            this.show_view_pane();
722
            view.show();
723
            
724
            // update menus
725
            if (this.select_view) {
726
                this.select_view.update_layout();
727
            }
728
            this.current_view.__update_layout();
729

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

    
753
        reset_vm_actions: function() {
754
        
755
        },
756
        
757
        // identify current view
758
        // based on views element visibility
759
        current_view_id: function() {
760
            var found = false;
761
            _.each(this.views, function(key, value) {
762
                if (value.visible()) {
763
                    found = value;
764
                }
765
            })
766
            return found;
767
        }
768

    
769
    });
770

    
771
    snf.ui.main = new views.MainView();
772
    
773
    snf.ui.logout = function() {
774
        $.cookie("X-Auth-Token", null);
775
        if (window.LOGOUT_REDIRECT !== undefined)
776
        {
777
            window.location = window.LOGOUT_REDIRECT;
778
        } else {
779
            window.location.reload();
780
        }
781
    }
782

    
783
    snf.ui.init = function() {
784
        snf.ui.main.load();
785
    }
786

    
787
})(this);