Statistics
| Branch: | Tag: | Revision:

root / snf-app / synnefo / ui / static / snf / js / ui / web / ui_list_view.js @ 483c9197

History | View | Annotate | Download (17.3 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
    var util = snf.util = snf.util || {};
12

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

    
15
    // shortcuts
16
    var bb = root.Backbone;
17
    
18
    var hasKey = Object.prototype.hasOwnProperty;
19

    
20
    views.ListMultipleActions = views.View.extend({
21
        
22
        view_id: "list_actions",
23

    
24
        initialize: function(view) {
25
            this.parent = view;
26
            this.el = this.parent.el;
27
           
28
            views.ListMultipleActions.__super__.initialize.call(this);
29
            this.set_handlers();
30
            this.update_layout();
31

    
32
            this.selected_action = undefined;
33
            this.available_actions = [];
34
            this.multi_view = synnefo.ui.main.multiple_actions_view;
35
            this.hovered = false;
36

    
37
            this.update_actions = _.throttle(this.update_actions, 100);
38
        },
39

    
40
        set_handlers: function() {
41
            var self = this;
42
            storage.vms.bind("change:pending_action", function() {
43
                if (!storage.vms.has_pending_actions()) {
44
                    self.parent.$(".actions a").removeClass("selected");
45
                    self.parent.clear_indicators();
46
                }
47
            });
48
            
49
            var self = this;
50
            this.parent.$(".actions a.enabled").live('click', function() {
51
                self.parent.$(".actions a").removeClass("selected");
52
                $(this).addClass("selected");
53
                self.parent.select_action($(this).attr("id").replace("action-",""));
54
            });
55

    
56
            this.parent.$(".actions a.enabled").live({
57
                'mouseenter': function() {
58
                    self.hovered = true;
59
                    self.parent.set_indicator_for($(this).attr("id").replace("action-",""));
60
                }, 
61
                'mouseleave': function() {
62
                    self.hovered = false;
63
                    self.parent.clear_indicators();
64
                }
65
            });
66
        },
67
        
68
        update_actions: function() {
69
            actions = undefined;
70
            this.available_actions = [];
71
            _.each(this.parent.get_selected_vms(), function(vm) {
72
                if (!actions) {
73
                    actions = vm.get_available_actions();
74
                    return;
75
                }
76
                actions = _.intersection(actions, vm.get_available_actions());
77
            });
78

    
79
            this.available_actions = actions;
80

    
81
            this.$(".actions a").removeClass("enabled");
82
            _.each(this.available_actions, _.bind(function(name){
83
                this.$("#action-" + name).addClass("enabled");
84
            }, this))
85
        },
86

    
87
        update_selected: function() {
88
            this.$("tr").removeClass("checked");
89
            this.$("tr input:checked").parent().parent().addClass("checked");
90
        },
91

    
92
        update_layout: function() {
93
            this.update_actions();
94
            this.update_selected();
95
        }
96
    });
97

    
98
    // VMs list view
99
    views.ListView = views.VMListView.extend({
100
        
101
        // view id (this could be used to identify 
102
        // the view object from global context
103
        view_id: 'vm_list',
104

    
105
        el: '#machinesview-list',
106
        id_tpl: 'list-vm-',
107
        link_id_tpl: 'list-vm-at-',
108

    
109
        hide_actions: false,
110

    
111
        selectors: {
112
            'vms': '.list-container',
113
            'vm': '#list-vm-',
114
            'view': '#machinesview-list',
115
            'tpl': '.list-container#machine-container-template',
116
            'spinner': '.large-spinner',
117
            'vm_spinner': '#list-vm-{0} .spinner',
118
            'vm_wave': '#list-vm-{0} .wave',
119
            'os_icon': '#list-vm-{0} .os_icon',
120
            'vm_cont_active': '#machinesview-list',
121
            'vm_cont_terminated': '#machinesview-list'
122
        },
123
        
124
        initialize: function() {
125
            this.current_vm = 0;
126
            
127
            // button selectors
128
            this.prev_button = this.$(".controls .previous");
129
            this.next_button = this.$(".controls .next");
130

    
131
            this.actions = this.$(".actions").show();
132
            this.datatable_cont = this.$(".dataTables_wrapper").show();
133
            this.content = this.$("#machinesview_content").show();
134
            this.filter = this.$(".dataTables_filter").show().css({'display':'block'});
135
            this.table_el = this.$(".list-machines").show();
136
            this.select_all = $("#list-view-select-all");
137

    
138
            this.actions = new views.ListMultipleActions(this);
139

    
140
            this.table = $("div.list table.list-machines").dataTable({
141
                "bInfo": false,
142
                "bRetrieve": true,
143
                "bPaginate": false,
144
                "bAutoWidth": false,
145
                "bSort": true,
146
                "bStateSave": true,
147
                "sScrollXInner": "500px",
148
                "aoColumnDefs": [
149
                    { "bSortable": false, "aTargets": [ 0 ] }
150
                ]
151
            });
152

    
153
            this.table_data = {};
154
            views.ListView.__super__.initialize.apply(this, arguments);
155

    
156
            this.update_layout = _.throttle(this.update_layout, 100);
157
        },
158
        
159
        reset: function() {
160
        },
161

    
162
        hide_actions: function() {
163
            this.$(".actions a").removeClass("selected");
164
        },
165

    
166
        // overload show function
167
        show_view: function() {
168
            this.log.debug("showing");
169
            this.sel('spinner').hide();
170
            this.__update_layout();
171
        },
172
        
173
        check_vm_container: function() {
174
        },
175

    
176
        // identify vm model instance id based on DOM element
177
        vm_id_for_element: function(el) {
178
            return el.attr('id').replace("list-vm-", "");
179
        },
180

    
181
        reset_actions: function() {
182
            this.$(".actions a").removeClass("selected");
183
            storage.vms.reset_pending_actions();
184
        },
185
        
186
        // set generic view handlers
187
        set_handlers: function() {
188
            this.$(".list-vm-checkbox").live('change', _.bind(function(){
189
                this.reset_actions();
190
                this.actions.update_layout();
191
                if (this.$("tbody input:checked").length > 0) {
192
                    this.select_all.attr("checked", true);
193
                } else {
194
                    this.select_all.attr("checked", false);
195
                }
196
                self.actions.update_layout();
197
            }, this))
198

    
199
            var self = this;
200
            this.select_all.click(function(){
201
                if ($(this).is(":checked")) {
202
                    self.$("tbody input").attr("checked", true);
203
                } else {
204
                    self.$("tbody input").attr("checked", false);
205
                }
206
                self.actions.update_layout();
207
            });
208
        },  
209

    
210
        get_selected_vms: function() {
211
            var selected = $(this.el).find(".list-vm-checkbox:checked");
212
            var vms = []
213
            _.each(selected, function(el){
214
                var id = parseInt($(el).attr("id").replace("checkbox-list-vm-", ""));
215
                vm = storage.vms.get(id);
216
                if (!vm) { return };
217
                vms.push(vm);
218
            });
219

    
220
            return vms;
221
        },
222

    
223
        select_action: function(action) {
224
            this.reset_actions();
225
            this.$(".actions a#action-" + action).addClass("selected");
226
            var vms = this.get_selected_vms();
227
            _.each(vms, function(vm){
228
                vm.update_pending_action(action);
229
            })
230
        },
231

    
232
        reset: function() {
233
            this.reset_actions();
234
        },
235

    
236
        create_vm: function(vm) {
237
            params = this.get_vm_table_data(vm);
238
            var index = this.table.fnAddData.call(this.table, params);
239
            this.table_data["vm_" + vm.id] = {index: index[0], params: params};
240
            // append row id
241
            $(this.table.fnGetNodes(index)).attr("id", this.id_tpl + vm.id);
242
                
243
            var vm_el = $("#" + this.id_tpl + vm.id);
244
            this._vm_els[vm.id] = vm_el;
245
            // hide indicators on creation
246
            this.vm(vm).find(".spinner").hide();
247
            this.vm(vm).find(".wave").hide();
248
            this.vm(vm).find(".os_icon").show();
249
            this.vm(vm).find(".action-indicator").hide();
250
            
251
            // ancestor method
252
            this.__set_vm_handlers(vm);
253
            this.post_add(vm);
254
            return this.vm(vm);
255
        },
256

    
257
        // remove vm
258
        remove_vm: function(vm) {
259
            this.vm(vm).find("input[type=checkbox]").removeAttr("checked");
260
            var vm_data = this.table_data["vm_" + vm.id];
261

    
262
            // update triggered on removed vm, skipping
263
            if (!vm_data) { return };
264

    
265
            var index = vm_data.index;
266
            this.table.fnDeleteRow(index);
267
            delete this.table_data["vm_" + vm.id];
268
            this.update_data();
269

    
270
            if (hasKey.call(this._vm_els, vm.id)) {
271
                delete this._vm_els[vm.id];
272
            }
273
        },
274

    
275
        update_data: function() {
276
            var new_data = this.table.fnGetData();
277
            _.each(new_data, _.bind(function(row, i){
278
                this.table_data["vm_" + row[5]].index = i;
279
                this.table_data["vm_" + row[5]].params = row;
280
            }, this));
281
        },
282

    
283
        set_indicator_for: function(action) {
284
            var vms = this.get_selected_vms();
285
            _.each(vms, _.bind(function(vm){
286
                var vmel = this.vm(vm);
287
                vmel.find("img.spinner, img.wave, img.os_icon").hide();
288
                vmel.find("span.action-indicator").show().removeClass().addClass(action + " action-indicator");
289
            }, this));
290
        },
291

    
292
        clear_indicators: function() {
293
            var vms = storage.vms.models;
294
            _.each(vms, _.bind(function(vm){
295
                var vmel = this.vm(vm);
296

    
297
                vmel.find("img.wave").hide();
298
                
299
                if (vm.pending_action) {
300
                    vmel.find("img.os_icon").hide();
301
                    vmel.find("span.action-indicator").show().removeClass().addClass(vm.pending_action + " action-indicator");
302
                    return;
303
                }
304

    
305
                if (vm.in_transition()) {
306
                    vmel.find("span.action-indicator").hide();
307
                    vmel.find("img.spinner").show();
308
                    return;
309
                }
310

    
311
                if (!this.actions.hovered) {
312
                    vmel.find("img.os_icon").show();
313
                    vmel.find("span.action-indicator").hide();
314
                    vmel.find("img.spinner").hide();
315
                }
316
                
317

    
318
            }, this));
319
        },
320

    
321
        get_vm_table_data: function(vm) {
322
            var checkbox = '<input type="checkbox" class="' + 
323
                views.ListView.STATE_CLASSES[vm.state()].join(" ") + 
324
                ' list-vm-checkbox" id="checkbox-' + this.id_tpl + vm.id + '"/>';
325

    
326
            var img = '<img class="os_icon" src="'+ this.get_vm_icon_path(vm, "small") +'" />';
327
            img = img + '<img src="'+snf.config.indicators_icons_url+'small/progress.gif" class="spinner" />';
328
            img = img + '<img src="'+snf.config.indicators_icons_url+'medium/wave.gif" class="wave" />';
329
            img = img + '<span class="action-indicator" />';
330

    
331
            var name = util.truncate(vm.get('name'), 25);
332
            var flavor = vm.get_flavor().details_string();
333
            var status = STATE_TEXTS[vm.state()];
334
            
335
            return [checkbox, img, name, flavor, status, vm.id];
336
        },
337

    
338
        post_add: function(vm) {
339
        },
340

    
341
        // is vm in transition ??? show the progress spinner
342
        update_transition_state: function(vm) {
343
            if (!vm) { return };
344
            if (this.in_transition) { return };
345
            
346
            if ((this.actions.hovered && this.vm(vm).find("input").is(":checked")) || vm.pending_action) {
347
                this.sel('vm_spinner', vm.id).hide();
348
                this.sel('vm_wave', vm.id).hide();
349
                this.sel('os_icon', vm.id).hide();
350
                this.vm(vm).find(".action-indicator").show();
351
                return;
352
            }
353

    
354
            if (vm.in_transition()){
355
                this.sel('vm_spinner', vm.id).show();
356
                this.sel('vm_wave', vm.id).hide();
357
                this.sel('os_icon', vm.id).hide();
358
                this.vm(vm).find(".action-indicator").hide();
359
            } else {
360
                this.sel('vm_spinner', vm.id).hide();
361
                this.vm(vm).find(".action-indicator").hide();
362
                this.sel('os_icon', vm.id).show();
363
            }
364
        },
365

    
366
        // display transition animations
367
        show_transition: function(vm) {
368
            this.in_transition = true;
369

    
370
            if (!this.visible()) { 
371
                this.in_transition = false; 
372
                this.update_transition_state(); 
373
                return 
374
            };
375

    
376
            var wave = this.sel('vm_wave', vm.id);
377
            if (!wave.length) {
378
                this.in_transition = false
379
                return
380
            }
381
            
382
            this.sel('vm_spinner', vm.id).hide();
383
            this.sel('os_icon', vm.id).hide();
384

    
385
            var src = wave.attr('src');
386
            var self = this;
387
            
388
            // change src to force gif play from the first frame
389
            // animate for 500 ms then hide
390
            wave.attr('src', "").show().attr('src', src).fadeIn(500).delay(700).fadeOut(300, function() {
391
                self.in_transition = false;
392
                self.update_transition_state(vm);
393
            });
394
        },
395

    
396
        update_actions_layout: function(vm) {
397
        },
398

    
399
        post_update_vm: function(vm) {
400
            
401
            // skip update for these changes for performance issues
402
            if (vm.hasOnlyChange(["pending_action", "stats"])) { return };
403

    
404
            var index = this.table_data["vm_" + vm.id].index;
405
            params = this.get_vm_table_data(vm);
406
            this.table_data["vm_" + vm.id].params = params;
407
            data = this.table.fnGetData()[index];
408

    
409
            // do not recreate checkboxes and images to avoid messing
410
            // with user interaction
411
            this.table.fnUpdate(params[2], parseInt(index), 2);
412
            this.table.fnUpdate(params[3], parseInt(index), 3);
413
            this.table.fnUpdate(params[4], parseInt(index), 4);
414

    
415
            var active_class = vm.is_active() ? "active" : "inactive";
416
            this.vm(vm).removeClass("active").removeClass("inactive").addClass(active_class);
417
            $(this.vm(vm).find("td").get(4)).addClass("status");
418
            $(this.vm(vm).find("td").get(3)).addClass("flavor");
419
            $(this.vm(vm).find("td").get(2)).addClass("name");
420

    
421
            if (vm.status() == "ERROR") {
422
                this.vm(vm).removeClass("active").removeClass("inactive").addClass("error");
423
            } else {
424
                this.vm(vm).removeClass("error").addClass(active_class);
425
            }
426
            
427
            this.update_os_icon(vm);
428
            this.update_transition_state(vm);
429
        },
430

    
431
        update_os_icon: function(vm) {
432
            this.sel('os_icon', vm.id).attr('src', this.get_vm_icon_path(vm, "small"));
433
        },
434
        
435
        // vm specific event handlers
436
        set_vm_handlers: function(vm) {
437
        },
438

    
439
        // generic stuff to do on each view update
440
        // called once after each vm has been updated
441
        update_layout: function() {
442
            this.actions.update_layout();
443
        },
444

    
445
        // update vm details
446
        update_details: function(vm) {
447
        },
448
            
449
        get_vm_icon_os: function(vm) {
450
            var os = vm.get_os();
451
            var icons = window.os_icons || views.ListView.VM_OS_ICONS;
452
            if (icons.indexOf(os) == -1) {
453
                os = "okeanos";
454
            }
455
            return os;
456
        },
457

    
458
        // TODO: move to views.utils (the method and the VM_OS_ICON vars)
459
        get_vm_icon_path: function(vm, icon_type) {
460
            var os = vm.get_os();
461
            var icons = window.os_icons || views.ListView.VM_OS_ICONS;
462

    
463
            if (icons.indexOf(os) == -1) {
464
                os = "okeanos";
465
            }
466
            
467
            var st = "off";
468
            if (vm.is_active()) {
469
                st = "on"
470
            }
471

    
472
            return views.ListView.VM_OS_ICON_TPLS()[icon_type].format(os, st);
473
        }
474
    })
475

    
476
    views.ListView.VM_OS_ICON_TPLS = function() {
477
        return {
478
            "small": snf.config.machines_icons_url + "small/{0}-{1}.png"
479
        }
480
    }
481

    
482
    views.ListView.VM_OS_ICONS = window.os_icons || [];
483

    
484
    views.ListView.STATE_CLASSES = {
485
        'UNKNOWN':          ['error-state'],
486
        'BUILD':            ['build-state'],
487
        'REBOOT':           ['rebooting-state'],
488
        'STOPPED':          ['terminated-state'],
489
        'ACTIVE':           ['running-state'],
490
        'ERROR':            ['error-state'],
491
        'DELETED':          ['destroying-state'],
492
        'DESTROY':          ['destroying-state'],
493
        'BUILD_INIT':       ['build-state'], 
494
        'BUILD_COPY':       ['build-state'],
495
        'BUILD_FINAL':      ['build-state'],
496
        'SHUTDOWN':         ['shutting-state'],
497
        'START':            ['starting-state'],
498
        'CONNECT':          ['connecting-state'],
499
        'DISCONNECT':       ['disconnecting-state']
500
    };
501

    
502
})(this);