Statistics
| Branch: | Tag: | Revision:

root / ui / static / synnefo.js @ 0e8e87ac

History | View | Annotate | Download (74 kB)

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

    
40

    
41
var error_timeout = 20000;
42
$.ajaxSetup({
43
    'beforeSend': function(xhr) {
44
          xhr.setRequestHeader("X-Auth-Token", $.cookie("X-Auth-Token"));
45
    },
46

    
47
    // catch uncaught error requests
48
    // stop interaction and show only for the 5xx errors
49
    // refresh the page after 20secs
50
    error: function(jqXHR, textStatus, errorThrown) {
51
        // stop interaction for important (aka 500) error codes only
52
        if (jqXHR.status >= 500 && jqXHR.status < 600)
53
        {
54
            try {
55
                ajax_error(jqXHR.status, undefined, 'Unknown', jqXHR.responseText);
56
            } catch(err) {
57
                ajax_error(-5, "UI Error", 'Unknown', err);
58
            }
59
        }
60

    
61
        // refresh after 10 seconds
62
        window.setTimeout("window.location.reload()", window.error_timeout);
63
    }
64
});
65

    
66

    
67
// jquery show/hide events
68
var _oldshow = $.fn.show;
69
$.fn.show = function(speed, callback) {
70
    $(this).trigger('show');
71
    return _oldshow.apply(this,arguments);
72
}
73
var _oldhide = $.fn.hide;
74
$.fn.hide = function(speed, callback) {
75
    $(this).trigger('hide');
76
    return _oldhide.apply(this,arguments);
77
}
78

    
79
function ISODateString(d){
80
    //return a date in an ISO 8601 format using UTC.
81
    //do not include time zone info (Z) at the end
82
    //taken from the Mozilla Developer Center
83
    function pad(n){ return n<10 ? '0'+n : n }
84
    return  d.getUTCFullYear()+ '-' +
85
            pad(d.getUTCMonth()+1) + '-' +
86
            pad(d.getUTCDate()) + 'T' +
87
            pad(d.getUTCHours()) + ':' +
88
            pad(d.getUTCMinutes()) + ':' +
89
            pad(d.getUTCSeconds()) +'Z'
90
}
91

    
92
function parse_error(responseText, errorCode){
93
    var errors = [];
94
    try {
95
        responseObj = JSON.parse(responseText);
96
    }
97
    catch(err) {
98
        errors[0] = {'code': errorCode};
99
        return errors;
100
    }
101
    for (var err in responseObj){
102
        errors[errors.length] = responseObj[err];
103
    }
104
    return errors;
105
}
106

    
107
// indexOf prototype for IE
108
if (!Array.prototype.indexOf) {
109
  Array.prototype.indexOf = function(elt /*, from*/) {
110
    var len = this.length;
111
    var from = Number(arguments[1]) || 0;
112
    from = (from < 0)
113
         ? Math.ceil(from)
114
         : Math.floor(from);
115
    if (from < 0)
116
      from += len;
117

    
118
    for (; from < len; from++) {
119
      if (from in this &&
120
          this[from] === elt)
121
        return from;
122
    }
123
    return -1;
124
  };
125
}
126

    
127
// trim prototype for IE
128
if(typeof String.prototype.trim !== 'function') {
129
    String.prototype.trim = function() {
130
        return this.replace(/^\s+|\s+$/g, '');
131
    }
132
}
133

    
134
function update_confirmations() {
135
    // hide all confirm boxes to begin with
136
    $('#machines-pane div.confirm_single').hide();
137
    $('#machines-pane div.confirm_multiple').hide();
138
    var action_type = [];
139
    // standard view or single view
140
    if ($.cookie("view") == '0' || $.cookie("view") == '2') {
141
        for (var i=0; i<pending_actions.length; i++) {
142
            // show single confirms
143
            if (pending_actions[i][0] == reboot) {
144
                action_type = "reboot";
145
            } else if (pending_actions[i][0] == shutdown) {
146
                action_type = "shutdown";
147
            } else if (pending_actions[i][0] == start) {
148
                action_type = "start";
149
            } else if (pending_actions[i][0] == open_console) {
150
                action_type = "console";
151
            } else {
152
                action_type = "destroy";
153
            }
154
            $("#machines-pane #" + pending_actions[i][1] +
155
            " div.action-container." + action_type + " div.confirm_single").show();
156
        }
157
    }
158
    // if more than one pending action show multiple confirm box
159
    if (pending_actions.length>1 || $.cookie("view") == '1' && pending_actions.length == 1){
160
        $('#machines-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
161
        $('#machines-pane div.confirm_multiple').show();
162
    }
163
}
164

    
165
function update_network_confirmations(){
166
    // hide all confirm boxes to begin with
167
    $('#networks-pane div.confirm_multiple').hide();
168

    
169
    for (var i=0;i<pending_actions.length;i++){
170
        // show single confirms depending on the action
171
        if (pending_actions[i][0] == delete_network) {
172
            $("#networks-pane div.network#net-"+pending_actions[i][1]).children('.confirm_single').show();
173
        } else if (pending_actions[i][0] == remove_server_from_network) {
174
            $("#networks-pane div.network #net-"+pending_actions[i][1]+"-server-"+pending_actions[i][2]).children('.confirm_single').show();
175
        } // else {}
176
    }
177

    
178
    // if more than one pending action show multiple confirm box
179
    if (pending_actions.length > 1){
180
        $('#networks-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
181
        $('#networks-pane div.confirm_multiple').show();
182
    }
183
}
184

    
185
function list_view() {
186
    changes_since = 0; // to reload full list
187
    pending_actions = []; // clear pending actions
188
    update_confirmations();
189
    clearTimeout(deferred);    // clear old deferred calls
190
    try {
191
        update_request.abort(); // cancel pending ajax updates
192
        load_request.abort();
193
    }catch(err){}
194
    $.cookie("view", '1'); // set list cookie
195
    uri = $("a#list").attr("href");
196
    load_request = $.ajax({
197
        url: uri,
198
        type: "GET",
199
        timeout: TIMEOUT,
200
        dataType: "html",
201
        error: function(jqXHR, textStatus, errorThrown) {
202
            return false;
203
        },
204
        success: function(data, textStatus, jqXHR) {
205
            $("a#list")[0].className += ' activelink';
206
            $("a#standard")[0].className = '';
207
            $("a#single")[0].className = '';
208
            $("div#machinesview").html(data);
209
        }
210
    });
211
    return false;
212
}
213

    
214
function single_view() {
215
    changes_since = 0; // to reload full list
216
    pending_actions = []; // clear pending actions
217
    update_confirmations();
218
    clearTimeout(deferred);    // clear old deferred calls
219
    try {
220
        update_request.abort(); // cancel pending ajax updates
221
        load_request.abort();
222
    }catch(err){}
223
    $.cookie("view", '2'); // set list cookie
224
    uri = $("a#single").attr("href");
225
    load_request = $.ajax({
226
        url: uri,
227
        type: "GET",
228
        timeout: TIMEOUT,
229
        dataType: "html",
230
        error: function(jqXHR, textStatus, errorThrown) {
231
            return false;
232
        },
233
        success: function(data, textStatus, jqXHR) {
234
            $("a#single")[0].className += ' activelink';
235
            $("a#standard")[0].className = '';
236
            $("a#list")[0].className = '';
237
            $("div#machinesview").html(data);
238
        }
239
    });
240
    return false;
241
}
242

    
243
function standard_view() {
244
    changes_since = 0; // to reload full list
245
    pending_actions = []; // clear pending actions
246
    update_confirmations();
247
    clearTimeout(deferred);    // clear old deferred calls
248
    try {
249
        update_request.abort() // cancel pending ajax updates
250
        load_request.abort();
251
    }catch(err){}
252
    $.cookie("view", '0');
253
    uri = $("a#standard").attr("href");
254
    load_request = $.ajax({
255
        url: uri,
256
        type: "GET",
257
        timeout: TIMEOUT,
258
        dataType: "html",
259
        error: function(jqXHR, textStatus, errorThrown) {
260
            return false;
261
        },
262
        success: function(data, textStatus, jqXHR) {
263
            $("a#standard")[0].className += ' activelink';
264
            $("a#list")[0].className = '';
265
            $("a#single")[0].className = '';
266
            $("div#machinesview").html(data);
267
        }
268
    });
269
    return false;
270
}
271

    
272
function choose_view() {
273
    if ($.cookie("view")=='1') {
274
        list_view();
275
    } else if ($.cookie("view")=='2'){
276
        single_view();
277
    } else {
278
        standard_view();
279
    }
280
}
281

    
282
// return value from metadata key "OS", if it exists
283
function os_icon(metadata) {
284
    if (!metadata) {
285
        return 'okeanos';
286
    }
287
    if (metadata.values.OS == undefined || metadata.values.OS == '') {
288
        return 'okeanos';
289
    } else {
290
        if (os_icons.indexOf(metadata.values.OS) == -1) {
291
            return 'okeanos';
292
        } else {
293
            return metadata.values.OS;
294
        }
295
    }
296
}
297

    
298
function os_icon_from_value(metadata) {
299
    if (!metadata) {
300
        return 'okeanos';
301
    }
302
if (metadata == undefined || metadata == '') {
303
        return 'okeanos';
304
    } else {
305
        if (os_icons.indexOf(metadata) == -1) {
306
            return 'okeanos';
307
        } else {
308
            return metadata;
309
        }
310
    }
311
}
312

    
313
// get and show a list of running and terminated machines
314
function update_vms(interval) {
315
    try{ console.info('updating machines'); } catch(err){}
316
    var uri= API_URL + '/servers/detail';
317

    
318
    if (changes_since != 0)
319
        uri+='?changes-since='+changes_since
320

    
321
    update_request = $.ajax({
322
        cache: false,
323
        url: uri,
324
        type: "GET",
325
        timeout: TIMEOUT,
326
        dataType: "json",
327
        error: function(jqXHR, textStatus, errorThrown) {
328
            // don't forget to try again later
329
            if (interval) {
330
                clearTimeout(deferred);    // clear old deferred calls
331
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
332
            }
333
            // as for now, just show an error message
334
            try { console.info('update_vms errback:' + jqXHR.status ) } catch(err) {}
335
            try {
336
                ajax_error(jqXHR.status, undefined, 'Update VMs', jqXHR.responseText);
337
            } catch(err) {
338
                ajax_error(-5, "UI Error", 'Update VMs', err);
339
            }
340
            return false;
341
            },
342
        success: function(data, textStatus, jqXHR) {
343
            // create changes_since string if necessary
344
            if (jqXHR.getResponseHeader('Date') != null){
345
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
346
                changes_since = ISODateString(changes_since_date);
347
            }
348

    
349
            if (interval) {
350
                clearTimeout(deferred);    // clear old deferred calls
351
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
352
            }
353

    
354
            if (jqXHR.status == 200 || jqXHR.status == 203) {
355
                try {
356
                    //servers = data.servers.values;
357
                    update_servers_data(data.servers.values, data);
358
                    update_machines_view(data);
359
                } catch(err) { ajax_error(-5, "UI Error", 'Update VMs', err);}
360
            } else if (jqXHR.status != 304){
361
                try { console.info('update_vms callback:' + jqXHR.status ) } catch(err) {}
362
                /*
363
                FIXME:  Here it should return the error, however Opera does not support 304.
364
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
365
                        304, which should be corrected (Bug #317).
366
                */
367
                //ajax_error(jqXHR.status, undefined, 'Update VMs', jqXHR.responseText);
368
            }
369
            return false;
370
        }
371
    });
372
    return false;
373
}
374

    
375
function update_servers_data(servers_update, data) {
376
    $(window).trigger("vm:update", servers_update, data);
377

    
378
    // first call
379
    if (!window.servers || window.servers.length == 0) {
380
        window.servers = servers_update;
381
        return;
382
    }
383
    
384
    // server exists helper
385
    server_exists = function(server) {
386
        var id = server.id;
387
        var found = false;
388
        var index = 0;
389
        $.each(servers, function(i, s) {
390
            if (s.id == id) { found = true, index = i };
391
        });
392
        if (found)
393
            return [found, index];
394

    
395
        return false;
396
    }
397

    
398
    // merge object properties
399
    merge = function() {
400
        var initial = arguments[0];
401
        var status_changed = undefined;
402
        $.each(arguments, function(index, el) {
403
            $.each(el, function(key,v) {
404
                // new attribute added
405
                var previous_value = initial[key];
406
                var v = v;
407
                if (initial[key] == undefined) {
408
                    $(window).trigger("vm:attr:add", initial, key, v);
409
                } else {
410
                    // value changed
411
                    if (initial[key] != v) {
412
                        if (key == "status") {
413
                            // dont change if in destroy state
414
                            if (initial.status == "DESTROY") {
415
                                v = "DESTROY";
416
                            }
417
                            status_changed = {'old': previous_value, 'new': v}; 
418
                        }
419
                        $(window).trigger("vm:attr:change", {'initial': initial, 'attr': key, 'newvalue': v});
420
                    }
421
                }
422
                initial[key] = v;
423
            });
424
        });
425
        if (status_changed !== undefined) {
426
            $(window).trigger('vm:status:change', {'vm': initial, 'old': status_changed['old'], 'new': status_changed['new']});
427
        }
428
        return initial;
429
    }
430
    
431
    // server removed
432
    var remove = [];
433
    $.each(servers_update, function(index, server) {
434
        if (server.status == "DELETED") {
435
            remove.push(server.id);
436
        }
437
    });
438
    
439
    // check server, if exists merge it with new values else add it
440
    $.each(servers_update, function(index, server) {
441
        var exists = server_exists(server);
442
        if (exists !== false) {
443
            try {
444
                servers[exists[1]] = merge(servers[exists[1]], server);
445
            } catch (err) {
446
            }
447
        } else {
448
            servers.push(server);
449
            $(window).trigger("vm:add", server);
450
        }
451
        if (remove.indexOf(server.id) > -1) {
452
            var remove_exists = server_exists(server);
453
            servers.splice(remove_exists[1], 1);
454
            $(window).trigger("vm:remove", server);
455
        }
456
    });
457
}
458

    
459
// get a list of running and terminated machines, used in network view
460
function update_networks(interval) {
461
    try{ console.info('updating networks'); } catch(err){}
462
    var uri= API_URL + '/servers/detail';
463

    
464
    if (changes_since != 0)
465
        uri+='?changes-since='+changes_since
466

    
467
    update_request = $.ajax({
468
        cache: false,
469
        url: uri,
470
        type: "GET",
471
        timeout: TIMEOUT,
472
        dataType: "json",
473
        error: function(jqXHR, textStatus, errorThrown) {
474
            // don't forget to try again later
475
            if (interval) {
476
                clearTimeout(deferred);    // clear old deferred calls
477
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
478
            }
479
            // as for now, just show an error message
480
            try { console.info('update_networks errback:' + jqXHR.status ) } catch(err) {}
481
            try {
482
                ajax_error(jqXHR.status, undefined, 'Update networks', jqXHR.responseText);
483
            } catch(err) {
484
                ajax_error(-5, "UI Error", 'Update networks', err);
485
            }
486
            return false;
487
            },
488
        success: function(data, textStatus, jqXHR) {
489
            // create changes_since string if necessary
490
            if (jqXHR.getResponseHeader('Date') != null){
491
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
492
                changes_since = ISODateString(changes_since_date);
493
            }
494

    
495
            if (interval) {
496
                clearTimeout(deferred);    // clear old deferred calls
497
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
498
            }
499

    
500
            if (jqXHR.status == 200 || jqXHR.status == 203) {
501
                try {
502
                    //servers = data.servers.values;
503
                    update_servers_data(data.servers.values, data);
504
                    jQuery.parseJSON(data);
505
                    update_network_names(data);
506
                } catch(err) { ajax_error(-5, "UI Error", 'Update networks', err);}
507
            } else if (jqXHR.status == 304) {
508
                update_network_names();
509
            }
510
            else {
511
                try { console.info('update_networks callback:' + jqXHR.status ) } catch(err) {}
512
                /*
513
                FIXME:  Here it should return the error, however Opera does not support 304.
514
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
515
                        304, which should be corrected (Bug #317).
516
                */
517
                //ajax_error(jqXHR.status, undefined, 'Update networks', jqXHR.responseText);
518
                update_network_names();
519
            }
520
            return false;
521
        }
522
    });
523
    return false;
524
}
525

    
526
// get and show a list of public and private networks
527
function update_network_names(servers_data) {
528
    try{ console.info('updating network names'); } catch(err){}
529
    var uri= API_URL + '/networks/detail';
530

    
531
    if (networks_changes_since != 0)
532
        //FIXME: Comment out the following, until metadata do not 304 when changed
533
        uri+='?changes-since=' + networks_changes_since
534

    
535
    update_request = $.ajax({
536
        cache: false,
537
        url: uri,
538
        type: "GET",
539
        timeout: TIMEOUT,
540
        dataType: "json",
541
        error: function(jqXHR, textStatus, errorThrown) {
542
            // as for now, just show an error message
543
            try {
544
                console.info('update_network names errback:' + jqXHR.status )
545
            } catch(err) {}
546
            try {
547
                ajax_error(jqXHR.status, undefined, 'Update network names', jqXHR.responseText);
548
            } catch(err) {
549
                ajax_error(-5, "UI Error", 'Update network names', err);
550
            }
551
            return false;
552
            },
553
        success: function(data, textStatus, jqXHR) {
554
            // create changes_since string if necessary
555
            if (jqXHR.getResponseHeader('Date') != null){
556
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
557
                networks_changes_since = ISODateString(changes_since_date);
558
            }
559

    
560
            if (jqXHR.status == 200 || jqXHR.status == 203) {
561
                try {
562
                    networks = data.networks.values;
563
                    jQuery.parseJSON(data);
564
                    update_networks_view(servers_data, data);
565
                } catch(err) {
566
                    ajax_error(-5, "UI Error", 'Update network names', err);
567
                }
568
            } else if (jqXHR.status == 304) {
569
                update_networks_view(servers_data);
570
            } else if (jqXHR.status != 304){
571
                try { console.info('update_network_names callback:' + jqXHR.status ) } catch(err) {}
572
                /*
573
                FIXME:  Here it should return the error, however Opera does not support 304.
574
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
575
                        304, which should be corrected (Bug #317).
576
                */
577
                //ajax_error(jqXHR.status, undefined, 'Update network names', jqXHR.responseText);
578
                update_networks_view(servers_data);
579
            }
580
            return false;
581
        }
582
    });
583
    return false;
584
}
585

    
586
// get and show a list of available standard and custom images
587
function update_images() {
588
    $.ajax({
589
        url: API_URL + '/images/detail',
590
        type: "GET",
591
        //async: false,
592
        dataType: "json",
593
        timeout: TIMEOUT,
594
        error: function(jqXHR, textStatus, errorThrown) {
595
                    try {
596
                        ajax_error(jqXHR.status, undefined, 'Update Images', jqXHR.responseText);
597
                    } catch(err) {
598
                        ajax_error(-5, "UI error", 'Update Images', err);
599
                    }
600
                },
601
        success: function(data, textStatus, jqXHR) {
602
            try {
603
                images = data.images.values;
604
                jQuery.parseJSON(data);
605
                update_wizard_images();
606
            } catch(err){
607
                ajax_error("NO_IMAGES");
608
            }
609
        }
610
    });
611
    return false;
612
}
613

    
614
function update_wizard_images() {
615
    if ($("ul#standard-images li").toArray().length + $("ul#custom-images li").toArray().length == 0) {
616
        $.each(images, function(i,image){
617
            var img = $('#image-template').clone().attr("id","img-"+image.id).fadeIn("slow");
618
            img.find("label").attr('for',"img-radio-" + image.id);
619
            img.find(".image-title").text(image.name);
620
            if (image.metadata) {
621
                if (image.metadata.values.description != undefined) {
622
                    img.find(".description").text(image.metadata.values.description);
623
                }
624
                if (image.metadata.values.size != undefined) {
625
                    img.find("#size").text(image.metadata.values.size);
626
                }
627
            }
628
            img.find("input.radio").attr('id',"img-radio-" + image.id);
629
            if (i==0) img.find("input.radio").attr("checked","checked");
630
            var image_logo = os_icon(image.metadata);
631
            img.find("img.image-logo").attr('src','static/icons/os/'+image_logo+'.png');
632
            if (image.metadata) {
633
                if (image.metadata.values.serverId != undefined) {
634
                    img.appendTo("ul#custom-images");
635
                } else {
636
                    img.appendTo("ul#standard-images");
637
                }
638
            } else {
639
                img.appendTo("ul#standard-images");
640
            }
641
        });
642
    }
643
}
644

    
645
function update_wizard_flavors(){
646
    // sliders for selecting VM flavor
647
    $("#cpu:range").rangeinput({min:0,
648
                               value:0,
649
                               step:1,
650
                               progress: true,
651
                               max:cpus.length-1});
652

    
653
    $("#storage:range").rangeinput({min:0,
654
                               value:0,
655
                               step:1,
656
                               progress: true,
657
                               max:disks.length-1});
658

    
659
    $("#ram:range").rangeinput({min:0,
660
                               value:0,
661
                               step:1,
662
                               progress: true,
663
                               max:ram.length-1});
664
    $("#small").click();
665

    
666
    // update the indicators when sliding
667
    $("#cpu:range").data().rangeinput.onSlide(function(event,value){
668
        $("#cpu-indicator")[0].value = cpus[Number(value)];
669
        $("#cpu-indicator").addClass('selectedrange');
670
    });
671
    $("#cpu:range").data().rangeinput.change(function(event,value){
672
        $("#cpu-indicator")[0].value = cpus[Number(value)];
673
        $("#custom").click();
674
        $("#cpu-indicator").removeClass('selectedrange');
675
    });
676
    $("#ram:range").data().rangeinput.onSlide(function(event,value){
677
        $("#ram-indicator")[0].value = ram[Number(value)];
678
        $("#ram-indicator").addClass('selectedrange');
679
    });
680
    $("#ram:range").data().rangeinput.change(function(event,value){
681
        $("#ram-indicator")[0].value = ram[Number(value)];
682
        $("#custom").click();
683
        $("#ram-indicator").removeClass('selectedrange');
684
    });
685
    $("#storage:range").data().rangeinput.onSlide(function(event,value){
686
        $("#storage-indicator")[0].value = disks[Number(value)];
687
        $("#storage-indicator").addClass('selectedrange');
688
    });
689
    $("#storage:range").data().rangeinput.change(function(event,value){
690
        $("#storage-indicator")[0].value = disks[Number(value)];
691
        $("#custom").click();
692
        $("#storage-indicator").removeClass('selectedrange');
693
    });
694
}
695

    
696
Array.prototype.unique = function () {
697
    var r = new Array();
698
    o:for(var i = 0, n = this.length; i < n; i++)
699
    {
700
        for(var x = 0, y = r.length; x < y; x++)
701
        {
702
            if(r[x]==this[i])
703
            {
704
                continue o;
705
            }
706
        }
707
        r[r.length] = this[i];
708
    }
709
    return r;
710
}
711

    
712
// get and configure flavor selection
713
function update_flavors() {
714
    $.ajax({
715
        url: API_URL + '/flavors/detail',
716
        type: "GET",
717
        //async: false,
718
        dataType: "json",
719
        timeout: TIMEOUT,
720
        error: function(jqXHR, textStatus, errorThrown) {
721
            try {
722
                ajax_error(jqXHR.status, undefined, 'Update Flavors', jqXHR.responseText);
723
            } catch (err) {
724
                ajax_error(-5, "UI Error", "Update Flavors", err);
725
            }
726
            // start updating vm list
727
            update_vms(UPDATE_INTERVAL);
728
        },
729
        success: function(data, textStatus, jqXHR) {
730

    
731
            try {
732
                flavors = data.flavors.values;
733
                jQuery.parseJSON(data);
734
                $.each(flavors, function(i, flavor) {
735
                    cpus[i] = flavor['cpu'];
736
                    disks[i] = flavor['disk'];
737
                    ram[i] = flavor['ram'];
738
                });
739
                cpus = cpus.unique();
740
                disks = disks.unique();
741
                ram = ram.unique();
742
                update_wizard_flavors();
743
            } catch(err){
744
                ajax_error("NO_FLAVORS");
745
            }
746
            // start updating vm list
747
            update_vms(UPDATE_INTERVAL);
748
        }
749
    });
750
    return false;
751
}
752

    
753
// return flavorRef from cpu, disk, ram values
754
function identify_flavor(cpu, disk, ram){
755
    for (i=0;i<flavors.length;i++){
756
        if (flavors[i]['cpu'] == cpu && flavors[i]['disk']==disk && flavors[i]['ram']==ram) {
757
            return flavors[i]['id']
758
        }
759
    }
760
    return 0;
761
}
762

    
763
// return image entry from imageRef
764
function get_image(imageRef) {
765
    for (i=0;i<images.length;i++){
766
        if (images[i]['id'] == imageRef) {
767
            return images[i];
768
        }
769
    }
770
    return 0;
771
}
772

    
773
// return machine entry from serverID
774
function get_machine(serverID) {
775
    for (i=0;i<servers.length;i++){
776
        if (servers[i]['id'] == serverID) {
777
            return servers[i];
778
        }
779
    }
780
    return 0;
781
}
782

    
783
// update the actions in icon view, per server
784
function update_iconview_actions(serverID, server_status) {
785
    if ($.cookie("view")=='2') {
786
        // remove .disable from all actions to begin with
787
        $('#machinesview-single #' + serverID + ' div.single-action').show();
788
        // decide which actions should be disabled
789
        for (current_action in actions) {
790
            if (actions[current_action].indexOf(server_status) == -1 ) {
791
                $('#machinesview-single #' + serverID + ' div.action-' + current_action).hide();
792
            }
793
        }
794
    } else {
795
        // remove .disable from all actions to begin with
796
        $('#machinesview-icon.standard #' + serverID + ' div.actions').find('a').removeClass('disabled');
797
        // decide which actions should be disabled
798
        for (current_action in actions) {
799
            if (actions[current_action].indexOf(server_status) == -1 ) {
800
                $('#machinesview-icon.standard #' + serverID + ' a.action-' + current_action).addClass('disabled');
801
            }
802
        }
803
    }
804
}
805

    
806
// update the actions in list view
807
function update_listview_actions() {
808
    var states = [];
809
    var on = [];
810
    var checked = $("table.list-machines tbody input[type='checkbox']:checked");
811
    // disable all actions to begin with
812
    $('#machinesview .list div.actions').children().removeClass('enabled');
813

    
814
    // are there multiple machines selected?
815
    if (checked.length>1)
816
        states[0] = 'multiple';
817

    
818
    // check the states of selected machines
819
    checked.each(function(i,checkbox) {
820
        states[states.length] = checkbox.className;
821
        var ip = $("#" + checkbox.id.replace('input-','') + ".ip span.public").text();
822
        if (ip.replace('undefined','').length)
823
            states[states.length] = 'network';
824
    });
825

    
826
    // decide which actions should be enabled
827
    for (a in actions) {
828
        var enabled = false;
829
        for (var s =0; s<states.length; s++) {
830
            if (actions[a].indexOf(states[s]) != -1 ) {
831
                enabled = true;
832
            } else {
833
                enabled = false;
834
                break;
835
            }
836
        }
837
        if (enabled)
838
            on[on.length]=a;
839
    }
840
    // enable those actions
841
    for (action in on) {
842
        $("#action-" + on[action]).addClass('enabled');
843
    }
844
}
845

    
846
//create server action
847
function create_vm(machineName, imageRef, flavorRef){
848
    var image_logo = os_icon(get_image(imageRef).metadata);
849
    var uri = API_URL + '/servers';
850
    var payload = {
851
        "server": {
852
            "name": machineName,
853
            "imageRef": imageRef,
854
            "flavorRef" : flavorRef,
855
            "metadata" : {
856
                "OS" : image_logo
857
            }
858
        }
859
    };
860

    
861
    $.ajax({
862
    url: uri,
863
    type: "POST",
864
    contentType: "application/json",
865
    dataType: "json",
866
    data: JSON.stringify(payload),
867
    timeout: TIMEOUT,
868
    error: function(jqXHR, textStatus, errorThrown) {
869
                // close wizard and show error box
870
                $('#machines-pane a#create').data('overlay').close();
871
                    try {
872
                        ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
873
                    } catch(err) {
874
                        ajax_error(-5, "UI Error", 'Create VM', err);
875
                    }
876
           },
877
    success: function(data, textStatus, jqXHR) {
878
                if ( jqXHR.status == '202') {
879
                    ajax_success("CREATE_VM_SUCCESS", data.server.adminPass);
880
                } else {
881
                    // close wizard and show error box
882
                    $('#machines-pane a#create').data('overlay').close();
883
                    ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
884
                }
885
            }
886
    });
887
}
888

    
889
// reboot action
890
function reboot(serverIDs){
891
    if (!serverIDs.length){
892
        //ajax_success('DEFAULT');
893
        return false;
894
    }
895
    // ajax post reboot call
896
    var payload = {
897
        "reboot": {"type" : "HARD"}
898
    };
899

    
900
    var serverID = serverIDs.pop();
901

    
902
    $.ajax({
903
        url: API_URL + '/servers/' + serverID + '/action',
904
        type: "POST",
905
        contentType: "application/json",
906
        dataType: "json",
907
        data: JSON.stringify(payload),
908
        timeout: TIMEOUT,
909
        error: function(jqXHR, textStatus, errorThrown) {
910
                    // in machine views
911
                    if ( $.cookie("pane") == 0) {
912
                        try {
913
                            display_failure(jqXHR.status, serverID, 'Reboot', jqXHR.responseText);
914
                        } catch (err) {
915
                            display_failure(0, serverID, 'Reboot', jqXHR.responseText);
916
                        }
917
                    }
918
                    // in network view
919
                    else {
920
                        try {
921
                            display_reboot_failure(jqXHR.status, serverID, jqXHR.responseText);
922
                        } catch (err) {
923
                            display_reboot_failure(0, serverID, jqXHR.responseText);
924
                        }
925
                    }
926
                },
927
        success: function(data, textStatus, jqXHR) {
928
                    if ( jqXHR.status == '202') {
929
                        try {
930
                            console.info('rebooted ' + serverID);
931
                        } catch(err) {}
932
                        // indicate that the action succeeded
933
                        // in machine views
934
                        if ( $.cookie("pane") == 0) {
935
                            display_success(serverID);
936
                        }
937
                        // in network view
938
                        else {
939
                            display_reboot_success(serverID);
940
                        }
941
                        // continue with the rest of the servers
942
                        reboot(serverIDs);
943
                    } else {
944
                        ajax_error(jqXHR.status, serverID, 'Reboot', jqXHR.responseText);
945
                    }
946
                }
947
    });
948
    return false;
949
}
950

    
951
// shutdown action
952
function shutdown(serverIDs) {
953
    if (!serverIDs.length){
954
        //ajax_success('DEFAULT');
955
        return false;
956
    }
957
    // ajax post shutdown call
958
    var payload = {
959
        "shutdown": {}
960
    };
961

    
962
    var serverID = serverIDs.pop();
963

    
964
    $.ajax({
965
        url: API_URL + '/servers/' + serverID + '/action',
966
        type: "POST",
967
        contentType: "application/json",
968
        dataType: "json",
969
        data: JSON.stringify(payload),
970
        timeout: TIMEOUT,
971
        error: function(jqXHR, textStatus, errorThrown) {
972
                    try {
973
                        display_failure(jqXHR.status, serverID, 'Shutdown', jqXHR.responseText);
974
                    } catch(err) {
975
                        display_failure(0, serverID, 'Shutdown', jqXHR.responseText);
976
                    }
977
                },
978
        success: function(data, textStatus, jqXHR) {
979
                    if ( jqXHR.status == '202') {
980
                        try {
981
                            console.info('suspended ' + serverID);
982
                        } catch(err) {}
983
                        // indicate that the action succeeded
984
                        display_success(serverID);
985
                        // continue with the rest of the servers
986
                        shutdown(serverIDs);
987
                    } else {
988
                        ajax_error(jqXHR.status, serverID, 'Shutdown', jqXHR.responseText);
989
                    }
990
                }
991
    });
992
    return false;
993
}
994

    
995
// destroy action
996
function destroy(serverIDs) {
997
    if (!serverIDs.length){
998
        //ajax_success('DEFAULT');
999
        return false;
1000
    }
1001
    // ajax post destroy call can have an empty request body
1002
    var payload = {};
1003

    
1004
    var serverID = serverIDs.pop();
1005

    
1006
    $.ajax({
1007
        url: API_URL + '/servers/' + serverID,
1008
        type: "DELETE",
1009
        contentType: "application/json",
1010
        dataType: "json",
1011
        data: JSON.stringify(payload),
1012
        timeout: TIMEOUT,
1013
        error: function(jqXHR, textStatus, errorThrown) {
1014
                    try {
1015
                        display_failure(jqXHR.status, serverID, 'Destroy', jqXHR.responseText);
1016
                    } catch(err) {
1017
                        display_failure(0, serverID, 'Destroy', jqXHR.responseText);
1018
                    }
1019
                },
1020
        success: function(data, textStatus, jqXHR) {
1021
                    if ( jqXHR.status == '204') {
1022
                        try {
1023
                            console.info('destroyed ' + serverID);
1024
                        } catch (err) {}
1025

    
1026
                        // update status on local storage object
1027
                        vm = get_machine(serverID);
1028
                        vm.status = "DESTROY";
1029

    
1030
                        // indicate that the action succeeded
1031
                        display_success(serverID);
1032
                        // continue with the rest of the servers
1033
                        destroy(serverIDs);
1034
                    } else {
1035
                        ajax_error(jqXHR.status, serverID, 'Destroy', jqXHR.responseText);
1036
                    }
1037
                }
1038
    });
1039
    return false;
1040
}
1041

    
1042
// start action
1043
function start(serverIDs){
1044
    if (!serverIDs.length){
1045
        //ajax_success('DEFAULT');
1046
        return false;
1047
    }
1048
    // ajax post start call
1049
    var payload = {
1050
        "start": {}
1051
    };
1052

    
1053
    var serverID = serverIDs.pop();
1054

    
1055
    $.ajax({
1056
        url: API_URL + '/servers/' + serverID + '/action',
1057
        type: "POST",
1058
        contentType: "application/json",
1059
        dataType: "json",
1060
        data: JSON.stringify(payload),
1061
        timeout: TIMEOUT,
1062
        error: function(jqXHR, textStatus, errorThrown) {
1063
                    try {
1064
                        display_failure(jqXHR.status, serverID, 'Start', jqXHR.responseText);
1065
                    } catch(err) {
1066
                        display_failure(0, serverID, 'Start', jqXHR.responseText);
1067
                    }
1068
                },
1069
        success: function(data, textStatus, jqXHR) {
1070
                    if ( jqXHR.status == '202') {
1071
                        try {
1072
                            console.info('started ' + serverID);
1073
                        } catch(err) {}
1074
                        // indicate that the action succeeded
1075
                        display_success(serverID);
1076
                        // continue with the rest of the servers
1077
                        start(serverIDs);
1078
                    } else {
1079
                        ajax_error(jqXHR.status, serverID, 'Start', jqXHR.responseText);
1080
                    }
1081
                }
1082
    });
1083
    return false;
1084
}
1085

    
1086
// Show VNC console
1087
function vnc_attachment(host, port, password) {
1088
    // FIXME: Must be made into parameters, in settings.py
1089
    //vnc = open("", "displayWindow",
1090
    //    "status=yes,toolbar=yes,menubar=yes");
1091
    vd = document.open("application/x-vnc");
1092

    
1093
    vd.writeln("[connection]");
1094
    vd.writeln("host=" + host);
1095
    vd.writeln("port=" + port);
1096
    vd.writeln("password=" + password);
1097

    
1098
    vd.close();
1099
}
1100

    
1101
// Show VNC console
1102
function show_vnc_console(serverID, serverName, serverIP, host, port, password) {
1103
    var params_url = '?machine=' + serverName + '&host_ip=' + serverIP + '&host=' + host + '&port=' + port + '&password=' + password;
1104
    var params_window = 'scrollbars=no,' +
1105
                        'menubar=no,' +
1106
                        'toolbar=no,' +
1107
                        'status=no,' +
1108
                        'top=0,' +
1109
                        'left=0,' +
1110
                        'height=' + screen.height + ',' +
1111
                        'width=' + screen.width + ',' +
1112
                        'fullscreen=yes';
1113

    
1114
    window.open('machines/console' + params_url, 'formresult' + serverID, params_window);
1115

    
1116
    // Restore os icon in list view
1117
    osIcon = $('#'+serverID).parent().parent().find('.list-logo');
1118
    osIcon.attr('src',osIcon.attr('os'));
1119
    return false;
1120
}
1121

    
1122
// console action
1123
function open_console(serverIDs){
1124
    if (!serverIDs.length){
1125
        //ajax_success('DEFAULT');
1126
        return false;
1127
    }
1128
    // ajax post start call
1129
    var payload = {
1130
        "console": {"type": "vnc"}
1131
    };
1132

    
1133
    var serverID = serverIDs.pop();
1134

    
1135
    var machine = get_machine(serverID);
1136
    var serverName = machine.name;
1137
    try {
1138
        var serverIP = machine.addresses.values[0].values[0].addr;
1139
    } catch(err) { var serverIP = 'undefined'; }
1140

    
1141
    $.ajax({
1142
        url: API_URL + '/servers/' + serverID + '/action',
1143
        type: "POST",
1144
        contentType: "application/json",
1145
        dataType: "json",
1146
        data: JSON.stringify(payload),
1147
        timeout: TIMEOUT,
1148
        error: function(jqXHR, textStatus, errorThrown) {
1149
                    try {
1150
                        display_failure(jqXHR.status, serverID, 'Console', jqXHR.responseText);
1151
                    } catch(err) {
1152
                        display_failure(0, serverID, 'Console', jqXHR.responseText);
1153
                    }
1154
                },
1155
        success: function(data, textStatus, jqXHR) {
1156
                    if ( jqXHR.status == '200') {
1157
                        try {
1158
                            console.info('got_console ' + serverID);
1159
                        } catch(err) {}
1160
                        // indicate that the action succeeded
1161
                        // show_vnc_console(serverID, serverName, serverIP,
1162
                        // data.console.host,data.console.port,data.console.password);
1163
                        show_vnc_console(serverID, serverName, serverIP,
1164
                                         data.console.host,data.console.port,data.console.password);
1165
                        display_success(serverID);
1166
                        // hide spinner
1167
                        $('#' + serverID + ' .spinner').hide();
1168
                        // continue with the rest of the servers
1169
                        open_console(serverIDs);
1170
                    } else {
1171
                        ajax_error(jqXHR.status, serverID, 'Console', jqXHR.responseText);
1172
                    }
1173
                }
1174
    });
1175
    return false;
1176
}
1177

    
1178
function vm_has_address(vmId) {
1179
    var vm = get_machine(vmId);
1180

    
1181
    if (!vm) return false;
1182

    
1183
    try {
1184
        var ip = vm.addresses.values[0].values[0].addr;
1185
    } catch (err) {
1186
        return false;
1187
    }
1188
    return ip;
1189
}
1190

    
1191
// connect to machine action
1192
function machine_connect(serverIDs){
1193
    if (!serverIDs.length){
1194
        //ajax_success('DEFAULT');
1195
        return false;
1196
    }
1197

    
1198
    var serverID = serverIDs.pop();
1199
    var machine = get_machine(serverID);
1200
    var serverName = machine.name;
1201
    
1202
    try {
1203
        var serverIP = machine.addresses.values[0].values[0].addr;
1204
    } catch(err) { var serverIP = 'undefined'; }
1205

    
1206
    try {
1207
        var os = machine.metadata.values.OS;
1208
    } catch(err) { var os = 'undefined'; }
1209

    
1210
    var params_url = '?ip_address=' + serverIP + '&os=' + os + "&host_os=" + $.client.os;
1211
    
1212
    if ($.client.os == "Windows" && os == "windows") {
1213
        window.open('machines/connect' + params_url + "&rdp=1");
1214
        return;
1215
    }
1216

    
1217
    try {
1218
        msg_box({title:'Connect to ' + serverName,content:'loading...',extra:'',
1219
        'ajax':'machines/connect' + params_url,
1220
        parse_data:function(data){
1221
            data.title = false;
1222
            data.content = data.info;
1223
            data.extra = "<a href='"+data.link.url+"'>"+data.link.title+"</a>";
1224
            return data;
1225
        }
1226
        });
1227
    } catch (error) {
1228
        window.open('machines/connect' + params_url);
1229
    }
1230

    
1231

    
1232
    // Restore os icon in list view
1233
    osIcon = $('#'+serverID).parent().parent().find('.list-logo');
1234
    osIcon.attr('src',osIcon.attr('os'));
1235

    
1236
    return false;
1237
}
1238

    
1239

    
1240
// rename server
1241
function rename(serverID, serverName){
1242
    if (!serverID.length){
1243
        //ajax_success('DEFAULT');
1244
        return false;
1245
    }
1246
    // ajax post rename call
1247
    var payload = {
1248
        "server": {"name": serverName}
1249
    };
1250

    
1251
    $.ajax({
1252
        url: API_URL + '/servers/' + serverID,
1253
        type: "PUT",
1254
        contentType: "application/json",
1255
        dataType: "json",
1256
        data: JSON.stringify(payload),
1257
        timeout: TIMEOUT,
1258
        error: function(jqXHR, textStatus, errorThrown) {
1259
                    try {
1260
                        display_failure(jqXHR.status, serverID, 'Rename', jqXHR.responseText);
1261
                    } catch(err) {
1262
                        display_failure(0, serverID, 'Rename', jqXHR.responseText);
1263
                    }
1264
                },
1265
        success: function(data, textStatus, jqXHR) {
1266
                    if ( jqXHR.status == '204' || jqXHR.status == '1223') {
1267
                        try {
1268
                            console.info('renamed ' + serverID);
1269
                        } catch(err) {}
1270
                        // indicate that the action succeeded
1271
                        display_success(serverID);
1272
                    } else {
1273
                        ajax_error(jqXHR.status, serverID, 'Rename', jqXHR.responseText);
1274
                    }
1275
                }
1276
    });
1277
    return false;
1278
}
1279

    
1280
// get server metadata
1281
function get_metadata(serverID, keys_only) {
1282
    $.ajax({
1283
        url: API_URL + '/servers/' + serverID + '/meta',
1284
        cache: false,
1285
        type: "GET",
1286
        //async: false,
1287
        dataType: "json",
1288
        timeout: TIMEOUT,
1289
        error: function(jqXHR, textStatus, errorThrown) {
1290
            try {
1291
                // close wizard and show error box
1292
                $("a#metadata-scrollable").data('overlay').close();
1293
                ajax_error(jqXHR.status, undefined, 'Get metadata', jqXHR.responseText);
1294
            } catch (err) {
1295
                ajax_error(-5, "UI Error", "Get metadata", err);
1296
            }
1297
        },
1298
        success: function(data, textStatus, jqXHR) {
1299
            // to list the new results in the edit dialog
1300
            if (keys_only) {
1301
                list_metadata_keys(serverID, data.metadata.values);
1302
            } else {
1303
                list_metadata(data);
1304
                list_metadata_keys(serverID, data.metadata.values);
1305
            }
1306
            //hide spinner
1307
            $('#metadata-wizard .large-spinner').hide();
1308
        }
1309
    });
1310
    return false;
1311
}
1312

    
1313
// delete metadata key-value pair
1314
function delete_metadata(serverID, meta_key) {
1315
    $.ajax({
1316
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1317
        type: "DELETE",
1318
        //async: false,
1319
        dataType: "json",
1320
        timeout: TIMEOUT,
1321
        error: function(jqXHR, textStatus, errorThrown) {
1322
            try {
1323
                // close wizard and show error box
1324
                $("a#metadata-scrollable").data('overlay').close();
1325
                ajax_error(jqXHR.status, undefined, 'Delete metadata', jqXHR.responseText);
1326
            } catch (err) {
1327
                ajax_error(-5, "UI Error", "Delete metadata", err);
1328
            }
1329
        },
1330
        success: function(data, textStatus, jqXHR) {
1331
                    // success: Do nothing, the UI is already updated
1332
        }
1333
    });
1334
    return false;
1335
}
1336

    
1337
// add metadata key-value pair
1338
function update_metadata(serverID, meta_key, meta_value) {
1339
    var payload = {
1340
        "meta": {
1341
        }
1342
    };
1343
    payload["meta"][meta_key] = meta_value;
1344

    
1345
    $.ajax({
1346
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1347
        type: "PUT",
1348
        contentType: "application/json",
1349
        dataType: "json",
1350
        data: JSON.stringify(payload),
1351
        timeout: TIMEOUT,
1352
        error: function(jqXHR, textStatus, errorThrown) {
1353
            try {
1354
                // close wizard and show error box
1355
                $("a#metadata-scrollable").data('overlay').close();
1356
                ajax_error(jqXHR.status, undefined, 'add metadata', jqXHR.responseText);
1357
            } catch (err) {
1358
                ajax_error(-5, "UI Error", "add metadata", err);
1359
            }
1360
        },
1361
        success: function(data, textStatus, jqXHR) {
1362
            // success: Update icons if meta key is OS
1363
            if (meta_key == "OS") {
1364
                $("#metadata-wizard .machine-icon").attr("src","static/icons/machines/small/" + os_icon_from_value(meta_value) + '-' + $("#metadata-wizard div#on-off").text() + '.png');
1365
                var machine_icon = $("#machinesview-icon").find("div#" + serverID);
1366
                var machine_single = $("#machinesview-single").find("div#" + serverID);
1367

    
1368
                var os = os_icon_from_value(meta_value);
1369
                var state = $("#metadata-wizard div#on-off").text()
1370
                var state_single = $(".state", machine_single).hasClass("terminated-state") ? "off" : "on";
1371
                set_machine_os_image(machine_icon, "icon", state, os);
1372
                set_machine_os_image(machine_single, "single", state_single, os);
1373
            }
1374
        }
1375
    });
1376
    return false;
1377
}
1378

    
1379
// get stats
1380
function get_server_stats(serverID) {
1381
    $.ajax({
1382
        url: API_URL + '/servers/' + serverID + '/stats',
1383
        cache: false,
1384
        type: "GET",
1385
        //async: false,
1386
        dataType: "json",
1387
        timeout: TIMEOUT,
1388
        error: function(jqXHR, textStatus, errorThrown) {
1389
                // report error as text inline
1390
                $('#' + serverID + ' img.busy').hide();
1391
                $('#' + serverID + ' div.stat-error').show();
1392
        },
1393
        success: function(data, textStatus, jqXHR) {
1394
            // in icon view
1395
            if ( $.cookie('view') == 0 ) {
1396
                $('#' + serverID + ' img.busy').removeClass('busy');
1397
                $('#' + serverID + ' img.cpu').attr("src", data.stats.cpuBar);
1398
                $('#' + serverID + ' img.net').attr("src", data.stats.netBar);
1399
            }
1400
            // in single view
1401
            else if ( $.cookie('view') == 2 ) {
1402
                $('#' + serverID + ' div.cpu-graph img.stats').attr("src", data.stats.cpuTimeSeries);
1403
                $('#' + serverID + ' div.network-graph img.stats').attr("src", data.stats.netTimeSeries);
1404
            }
1405
        }
1406
    });
1407
    return false;
1408
}
1409

    
1410
// create network
1411
function create_network(networkName){
1412
    // ajax post start call
1413
    var payload = {
1414
        "network": { "name": networkName }
1415
    };
1416

    
1417
    $.ajax({
1418
        url: API_URL + '/networks',
1419
        type: "POST",
1420
        contentType: "application/json",
1421
        dataType: "json",
1422
        data: JSON.stringify(payload),
1423
        timeout: TIMEOUT,
1424
        error: function(jqXHR, textStatus, errorThrown) {
1425
            try {
1426
                // close wizard and show error box
1427
                $("a#networkscreate").overlay().close();
1428
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1429
            } catch (err) {
1430
                ajax_error(-5, "UI Error", "Create network", err);
1431
            }
1432
        },
1433
        success: function(data, textStatus, jqXHR) {
1434
            if ( jqXHR.status == '202') {
1435
                try {
1436
                    console.info('created network ' + networkName);
1437
                } catch(err) {}
1438
                /*
1439
                On success of this call nothing happens.
1440
                When the UI gets the first update containing the created server,
1441
                the creation wizard is closed and the new network is inserted
1442
                to the DOM. This is done in update_networks_view()
1443
                */
1444
            } else {
1445
                // close wizard and show error box
1446
                $("a#networkscreate").overlay().close();
1447
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1448
            }
1449
        }
1450
    });
1451
    return false;
1452
}
1453

    
1454
// rename network
1455
function rename_network(networkID, networkName){
1456
    if (!networkID.length){
1457
        //ajax_success('DEFAULT');
1458
        return false;
1459
    }
1460
    // prepare payload
1461
    var payload = {
1462
        "network": {"name": networkName}
1463
    };
1464
    // ajax call
1465
    $.ajax({
1466
        url: API_URL + '/networks/' + networkID,
1467
        type: "PUT",
1468
        contentType: "application/json",
1469
        dataType: "json",
1470
        data: JSON.stringify(payload),
1471
        timeout: TIMEOUT,
1472
        error: function(jqXHR, textStatus, errorThrown) {
1473
            try {
1474
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1475
            } catch (err) {
1476
                ajax_error(-1, "UI Error", 'Rename network', err);
1477
            }
1478
        },
1479
        success: function(data, textStatus, jqXHR) {
1480
            if ( jqXHR.status == '204') {
1481
                try {
1482
                    console.info('renamed network' + networkID);
1483
                } catch(err) {}
1484
            } else {
1485
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1486
            }
1487
        }
1488
    });
1489
    return false;
1490
}
1491

    
1492
function delete_network(networkIDs){
1493
    if (!networkIDs.length){
1494
        //ajax_success('DEFAULT');
1495
        return false;
1496
    }
1497
    // get a network
1498
    var networkID = networkIDs.pop();
1499
    // ajax post destroy call can have an empty request body
1500
    var payload = {};
1501
    // ajax call
1502
    $.ajax({
1503
        url: API_URL + '/networks/' + networkID,
1504
        type: "DELETE",
1505
        contentType: "application/json",
1506
        dataType: "json",
1507
        data: JSON.stringify(payload),
1508
        timeout: TIMEOUT,
1509
        error: function(jqXHR, textStatus, errorThrown) {
1510
            try {
1511
                display_net_failure(jqXHR.status, networkID, 'Delete', jqXHR.responseText);
1512
            } catch (err) {
1513
                display_net_failure(0, networkID, 'Delete', jqXHR.responseText);
1514
            }
1515
        },
1516
        success: function(data, textStatus, jqXHR) {
1517
            if ( jqXHR.status == '204') {
1518
                try {
1519
                    console.info('deleted network ' + networkID);
1520
                } catch(err) {}
1521
                // continue with the rest of the servers
1522
                delete_network(networkIDs);
1523
            } else {
1524
                try {
1525
                    display_net_failure(jqXHR.status, networkID, 'Delete', jqXHR.responseText);
1526
                } catch (err) {
1527
                    display_net_failure(0, networkID, 'Delete', jqXHR.responseText);
1528
                }
1529
            }
1530
        }
1531
    });
1532
    return false;
1533
}
1534

    
1535
function add_server_to_network(networkID, serverIDs, serverNames, serverStates) {
1536
    if (!serverIDs.length){
1537
        // close the overlay when all the calls are made
1538
        $("a#add-machines-overlay").overlay().close();
1539
        return false;
1540
    }
1541
    // get a server
1542
    var serverID = serverIDs.pop();
1543
    var serverName = serverNames.pop();
1544
    var serverState = serverStates.pop();
1545
    // prepare payload
1546
    var payload = {
1547
            "add": { "serverRef": serverID }
1548
        };
1549
    // prepare ajax call
1550
    $.ajax({
1551
        url: API_URL + '/networks/' + networkID + '/action',
1552
        type: "POST",
1553
        contentType: "application/json",
1554
        dataType: "json",
1555
        data: JSON.stringify(payload),
1556
        timeout: TIMEOUT,
1557
        error: function(jqXHR, textStatus, errorThrown) {
1558
            try {
1559
                // close wizard and show error box
1560
                $("a#add-machines-overlay").data('overlay').close();
1561
                ajax_error(jqXHR.status, undefined, 'Add server to network', jqXHR.responseText);
1562
            } catch (err) {
1563
                ajax_error(-5, "UI Error", 'Add server to network', err);
1564
            }
1565
        },
1566
        success: function(data, textStatus, jqXHR) {
1567
            if ( jqXHR.status == '202') {
1568
                try {
1569
                    console.info('added server ' + serverID + ' to network ' + networkID);
1570
                } catch(err) {}
1571
                // toggle the reboot dialog
1572
                display_reboot_dialog(networkID, serverID, serverName, serverState);
1573
                // continue with the rest of the servers
1574
                add_server_to_network(networkID, serverIDs, serverNames, serverStates);
1575
            } else {
1576
                // close wizard and show error box
1577
                $("a#add-machines-overlay").data('overlay').close();
1578
                ajax_error(jqXHR.status, undefined, 'Add server to network', jqXHR.responseText);
1579
            }
1580
        }
1581
    });
1582
    return false;
1583
}
1584

    
1585
function remove_server_from_network(networkIDs, serverIDs, serverNames, serverStates) {
1586
    if (!networkIDs.length){
1587
        //ajax_success('DEFAULT');
1588
        return false;
1589
    }
1590
    // get a network and a server
1591
    var networkID = networkIDs.pop();
1592
    var serverID = serverIDs.pop();
1593
    var serverName = serverNames.pop();
1594
    var serverState = serverStates.pop();
1595
    // prepare payload
1596
    var payload = {
1597
            "remove": { "serverRef": serverID }
1598
        };
1599
    // prepare ajax call
1600
    $.ajax({
1601
        url: API_URL + '/networks/' + networkID + '/action',
1602
        type: "POST",
1603
        contentType: "application/json",
1604
        dataType: "json",
1605
        data: JSON.stringify(payload),
1606
        timeout: TIMEOUT,
1607
        error: function(jqXHR, textStatus, errorThrown) {
1608
            try {
1609
                ajax_error(jqXHR.status, undefined, 'Remove server form network', jqXHR.responseText);
1610
            } catch (err) {
1611
                ajax_error(-5, "UI Error", 'Remove server form network', err);
1612
            }
1613
        },
1614
        success: function(data, textStatus, jqXHR) {
1615
            if ( jqXHR.status == '202') {
1616
                try {
1617
                    console.info('deleted server ' + serverID + ' from network ' + networkID);
1618
                } catch(err) {}
1619
                // toggle the reboot dialog
1620
                display_reboot_dialog(networkID, serverID, serverName, serverState);
1621
                // continue with the rest of the servers
1622
                remove_server_form_network(networkIDs, serverIDs, serverNames, serverStates);
1623
            } else {
1624
                ajax_error(jqXHR.status, undefined, 'Remove server form network', jqXHR.responseText);
1625
            }
1626
        }
1627
    });
1628
    return false;
1629
}
1630

    
1631
function set_firewall(networkID, serverID, profile) {
1632
    if (!networkID.length || !serverID.length || !profile.length){
1633
        return false;
1634
    }
1635
    // prepare payload
1636
    var payload = {
1637
            "firewallProfile": { "profile": profile }
1638
        };
1639
    // prepare ajax call
1640
    $.ajax({
1641
        url: API_URL + '/servers/' + serverID + '/action',
1642
        type: "POST",
1643
        contentType: "application/json",
1644
        dataType: "json",
1645
        data: JSON.stringify(payload),
1646
        timeout: TIMEOUT,
1647
        error: function(jqXHR, textStatus, errorThrown) {
1648
            try {
1649
                ajax_error(jqXHR.status, undefined, 'Set firewall profile', jqXHR.responseText);
1650
            } catch (err) {
1651
                ajax_error(-5, "UI Error", 'Set firewall profile', err);
1652
            }
1653
        },
1654
        success: function(data, textStatus, jqXHR) {
1655
            if ( jqXHR.status == '202') {
1656
                try {
1657
                    console.info('for server ' + serverID + ' set firewall profile to ' + profile);
1658
                } catch(err) {}
1659
                //remove progress gif and toggle the content
1660
                $('div#net-' + networkID + '-server-' + serverID + ' button.firewall-apply').html(VARIOUS["APPLY"]);
1661
                $('div#net-' + networkID + '-server-' + serverID + ' button.firewall-apply').attr("disabled", false);
1662
                $('div#net-' + networkID + '-server-' + serverID + ' div.firewall-header').click();
1663
                // toggle the reboot dialog
1664
                var serverName = $('div#net-' + networkID + '-server-' + serverID + ' div.machine-name-div span.name').text();
1665
                var serverState = $('div#net-' + networkID + '-server-' + serverID + ' img.logo').attr('src').split('-')[1];
1666
                serverState = serverState.split('.')[0];
1667
                display_reboot_dialog(networkID, serverID, serverName, serverState);
1668
            } else {
1669
                ajax_error(jqXHR.status, undefined, 'Set firewall profile', jqXHR.responseText);
1670
            }
1671
        }
1672
    });
1673
    return false;
1674
}
1675

    
1676
// show the welcome screen
1677
function showWelcome() {
1678
    $("#view-select").fadeOut("fast");
1679
    $("#emptymachineslist").fadeIn("fast");
1680
    $("#machinesview").hide();
1681
}
1682

    
1683
// hide the welcome screen
1684
function hideWelcome() {
1685
    $("#emptymachineslist").fadeOut("fast");
1686
    $("#view-select").fadeIn("fast");
1687
    $("div#view-select").show();
1688
    $("#machinesview").show();
1689
}
1690

    
1691
function log_server_status_change(server_entry, new_status) {
1692
    // firebug console logging
1693
    try {
1694
        if ($("#machinesview-single").length > 0) {
1695
            console.info(server_entry.find("div.machine-details div.name").text() +
1696
                        ' from ' + server_entry.find(".state-label").text() +
1697
                        ' to ' + STATUSES[new_status]);
1698
        } else {
1699
            console.info(server_entry.find("div.name span.name").text() +
1700
                        ' from ' + server_entry.find(".status").text() +
1701
                        ' to ' + STATUSES[new_status]);
1702
        }
1703
    } catch(err) {}
1704
}
1705

    
1706
function get_flavor_params(flavorRef) {
1707
    var cpus, ram, disk;
1708
    if ( flavors.length > 0 ) {
1709
        var current_flavor = '';
1710
        for (i=0; i<flavors.length; i++) {
1711
            if (flavors[i]['id'] == flavorRef) {
1712
                current_flavor = flavors[i];
1713
            }
1714
        }
1715
        cpus = current_flavor['cpu'];
1716
        ram = current_flavor['ram'];
1717
        disk = current_flavor['disk'];
1718
    } else {
1719
        cpus = 'undefined';
1720
        ram = 'undefined';
1721
        disk = 'undefined';
1722
    }
1723
    return {'cpus': cpus, 'ram': ram, 'disk': disk};
1724
}
1725

    
1726
function get_image_params(imageRef) {
1727
    var image_name, image_size;
1728
    if ( images.length > 0 ) {
1729
        var current_image = '';
1730
        for (i=0; i<images.length; i++) {
1731
            if (images[i]['id'] == imageRef) {
1732
                current_image = images[i];
1733
            }
1734
        }
1735
        try {
1736
            image_name = current_image['name'];
1737
        } catch(err) { image_name = 'undefined'; }
1738
        try{
1739
            image_size = current_image['metadata']['values']['size'];
1740
        } catch(err) { image_size = 'undefined'; }
1741
    } else {
1742
        image_name = 'undefined';
1743
        image_size = 'undefined';
1744
    }
1745
    return {'name': image_name,'size': image_size};
1746
}
1747

    
1748
function get_public_ips(server) {
1749
    var ip4, ip6;
1750
    try {
1751
        if (server.addresses.values) {
1752
            $.each (server.addresses.values, function(i, value) {
1753
                if (value.id == 'public') {
1754
                    try {
1755
                        $.each (value.values, function(i, ip) {
1756
                            if (ip.version == '4') {
1757
                                ip4 = ip.addr;
1758
                            } else if (ip.version == '6') {
1759
                                ip6 = ip.addr;
1760
                            } else {
1761
                                ip4 = 'pending';
1762
                                ip6 = 'pending';
1763
                            }
1764
                        });
1765
                    } catch (err){
1766
                        try{console.info('Server ' + server.id + ' has invalid ips')}catch(err){};
1767
                        ip4 = 'pending';
1768
                        ip6 = 'pending';
1769
                    }
1770
                }
1771
            });
1772
        }
1773
    } catch (err) {
1774
        try{console.info('Server ' + server.id + ' has no network addresses')}catch(err){};
1775
        ip4 = 'pending';
1776
        ip6 = 'pending';
1777
    }
1778
    return {'ip4': ip4, 'ip6': ip6};
1779
}
1780

    
1781
function get_private_ips(server) {
1782

    
1783
}
1784

    
1785
function close_all_overlays() {
1786
        try {
1787
                $("a#networkscreate").overlay().close();
1788
        } catch(err) {}
1789
        try {
1790
                $('a#create').overlay().close();
1791
        } catch(err) {}
1792
        try {
1793
                $("a#add-machines-overlay").overlay().close();
1794
        } catch(err) {}
1795
        try {
1796
                $("a#metadata-scrollable").overlay().close();
1797
        } catch(err) {}
1798
        try {
1799
                $("a#msgbox").overlay().close();
1800
        } catch(err) {}
1801
}
1802

    
1803
// logout
1804
function user_session_logout() {
1805
    $.cookie("X-Auth-Token", null);
1806
    if (window.LOGOUT_REDIRECT !== undefined)
1807
    {
1808
        window.location = window.LOGOUT_REDIRECT;
1809
    } else {
1810
        window.location.reload();
1811
    }
1812
}
1813

    
1814
// action indicators
1815
function init_action_indicator_handlers(machines_view)
1816
{
1817
    // init once for each view
1818
    if (window.ACTION_ICON_HANDLERS == undefined)
1819
    {
1820
        window.ACTION_ICON_HANDLERS = {};
1821
    }
1822

    
1823
    if (machines_view in window.ACTION_ICON_HANDLERS)
1824
    {
1825
        return;
1826
    }
1827
    window.ACTION_ICON_HANDLERS[machines_view] = 1;
1828

    
1829
    if (machines_view == "list")
1830
    {
1831
        // totally different logic for list view
1832
        init_action_indicator_list_handlers();
1833
        return;
1834
    }
1835

    
1836
    function update_action_icon_indicators(force)
1837
    {
1838
        function show(el, action) {
1839
            $(".action-indicator", $(el)).attr("class", "action-indicator " + action);
1840
            $(".action-indicator", $(el)).show();
1841
        }
1842

    
1843
        function hide(el) {
1844
            $(".action-indicator", $(el)).hide();
1845
        }
1846

    
1847
        function get_pending_actions(el) {
1848
            return $(".confirm_single:visible", $(el));
1849
        }
1850

    
1851
        function other_indicators(el) {
1852
           return $("img.wave:visible, img.spinner:visible", $(el))
1853
        }
1854

    
1855
        $("div.machine:visible, div.single-container").each(function(index, el){
1856
            var el = $(el);
1857
            var pending = get_pending_actions(el);
1858
            var other = other_indicators(el);
1859
            var action = undefined;
1860
            var force_action = force;
1861
            var visible = $(el).css("display") == "block";
1862

    
1863
            if (force_action !==undefined && force_action.el !== el[0]) {
1864
                // force action for other vm
1865
                // skipping force action
1866
                force_action = undefined;
1867
            }
1868

    
1869
            if (force_action !==undefined && force_action.el === el[0]) {
1870
                action = force_action.action;
1871
            }
1872

    
1873
            if (other.length >= 1) {
1874
                return;
1875
            }
1876

    
1877
            if (pending.length >= 1 && force_action === undefined) {
1878
                action = $(pending.parent()).attr("class").replace("action-container","");
1879
            }
1880

    
1881
            if (action in {'console':''}) {
1882
                return;
1883
            }
1884

    
1885
            if (action !== undefined) {
1886
                show(el, action);
1887
            } else {
1888
                try {
1889
                    if (el.attr('id') == pending_actions[0][1])
1890
                    {
1891
                        return;
1892
                    }
1893
                } catch (err) {
1894
                }
1895
                hide(el);
1896
            }
1897

    
1898
        });
1899
    }
1900

    
1901
    // action indicators
1902
    $(".action-container").live('mouseover', function(evn) {
1903
        force_action = {'el': $(evn.currentTarget).parent().parent()[0], 'action':$(evn.currentTarget).attr("class").replace("action-container","")};
1904
        // single view case
1905
        if ($(force_action.el).attr("class") == "upper")
1906
        {
1907
            force_action.el = $(evn.currentTarget).parent().parent().parent()[0]
1908
        };
1909
        update_action_icon_indicators(force_action);
1910
    });
1911

    
1912
    $("img.spinner, img.wave").live('hide', function(){
1913
        update_action_icon_indicators();
1914
    });
1915
    // register events where icons should get updated
1916

    
1917
    // hide action indicator image on mouse out, spinner appear, wave appear
1918
    $(".action-container").live("mouseout", function(evn){
1919
        update_action_icon_indicators();
1920
    });
1921

    
1922
    $(".confirm_single").live("click", function(evn){
1923
        update_action_icon_indicators();
1924
    });
1925

    
1926
    $("img.spinner, img.wave").live('show', function(){
1927
        $("div.action-indicator").hide();
1928
    });
1929

    
1930
    $(".confirm_single button.no").live('click', function(evn){
1931
        $("div.action-indicator", $(evn.currentTarget).parent().parent()).hide();
1932
    });
1933

    
1934
    $(".confirm_multiple button.no").click(function(){
1935
        $("div.action-indicator").hide();
1936
    });
1937

    
1938
    $(".confirm_multiple button.yes").click(function(){
1939
        $("div.action-indicator").hide();
1940
    });
1941
}
1942

    
1943
function init_action_indicator_list_handlers()
1944
{
1945
    var skip_actions = { 'console':'','connect':'','details':'' };
1946

    
1947
    var has_pending_confirmation = function()
1948
    {
1949
        return $(".confirm_multiple:visible").length >= 1
1950
    }
1951

    
1952
    function update_action_indicator_icons(force_action, skip_pending)
1953
    {
1954
        // pending action based on the element class
1955
        var pending_action = $(".selected", $(".actions"))[0];
1956
        var selected = get_list_view_selected_machine_rows();
1957

    
1958
        // reset previous state
1959
        list_view_hide_action_indicators();
1960

    
1961
        if (pending_action == undefined && !force_action)
1962
        {
1963
            // no action selected
1964
            return;
1965
        }
1966

    
1967
        if (force_action != undefined)
1968
        {
1969
            // user forced action choice
1970
            var action_class = force_action;
1971
        } else {
1972
            // retrieve action name (reboot, stop, etc..)
1973
            var action_class = $(pending_action).attr("id").replace("action-","");
1974
        }
1975

    
1976
        selected.each(function(index, el) {
1977
            if (has_pending_confirmation() && skip_pending)
1978
            {
1979
                return;
1980
            }
1981
            var el = $(el);
1982
            var logo = $("img.list-logo", el);
1983
            $(".action-indicator", el).remove();
1984
            var cls = "action-indicator " + action_class;
1985
            // add icon div
1986
            logo.after('<div class="' + cls + '"></div>');
1987
            // hide os logo
1988
            $("img.list-logo", el).hide();
1989
        });
1990
    }
1991

    
1992
    // on mouseover we force the images to the hovered action
1993
    $(".actions a").live("mouseover", function(evn) {
1994
        var el = $(evn.currentTarget);
1995
        if (!el.hasClass("enabled"))
1996
        {
1997
            return;
1998
        }
1999
        var action_class = el.attr("id").replace("action-","");
2000
        if (action_class in skip_actions)
2001
        {
2002
            return;
2003
        }
2004
        update_action_indicator_icons(action_class, false);
2005
    });
2006

    
2007

    
2008
    // register events where icons should get updated
2009
    $(".actions a.enabled").live("click", function(evn) {
2010
        // clear previous selections
2011
        $("a.selected").removeClass("selected");
2012

    
2013
        var el = $(evn.currentTarget);
2014
        el.addClass("selected");
2015
        update_action_indicator_icons(undefined, false);
2016
    });
2017

    
2018
    $(".actions a").live("mouseout", function(evn) {
2019
        update_action_indicator_icons(undefined, false);
2020
    });
2021

    
2022
    $(".confirm_multiple button.no").click(function(){
2023
        list_view_hide_action_indicators();
2024
    });
2025

    
2026
    $(".confirm_multiple button.yes").click(function(){
2027
        list_view_hide_action_indicators();
2028
    });
2029

    
2030
    $("input[type=checkbox]").live('change', function(){
2031
        // pending_actions will become empty on every checkbox click/change
2032
        // line 154 machines_list.html
2033
        pending_actions = [];
2034
        if (pending_actions.length == 0)
2035
        {
2036
            $(".confirm_multiple").hide();
2037
            $("a.selected").each(function(index, el){$(el).removeClass("selected")});
2038
        }
2039
        update_action_indicator_icons(undefined, false);
2040
    });
2041

    
2042
}
2043

    
2044
function list_view_hide_action_indicators()
2045
{
2046
    $("tr td .action-indicator").remove();
2047
    $("tr td img.list-logo").show();
2048
}
2049

    
2050
function get_list_view_selected_machine_rows()
2051
{
2052
    var table = $("table.list-machines");
2053
    var rows = $("tr:has(input[type=checkbox]:checked)",table);
2054
    return rows;
2055
}
2056

    
2057
// machines images utils
2058
function set_machine_os_image(machine, machines_view, state, os, skip_reset_states, remove_state) {
2059
    var views_map = {'single': '.single-image', 'icon': '.logo'};
2060
    var states_map = {'on': 'state1', 'off': 'state3', 'hover': 'state4', 'click': 'state2'}
2061
    var sizes_map = {'single': 'large', 'icon': 'medium'}
2062

    
2063
    var size = sizes_map[machines_view];
2064
    var img_selector = views_map[machines_view];
2065
    var cls = states_map[state];
2066

    
2067
    if (os === "unknown") { os = "okeanos" } ;
2068
    var new_img = 'url("./static/icons/machines/' + size + '/' + os + '-sprite.png")';
2069

    
2070
    var el = $(img_selector, machine);
2071
    var current_img = el.css("backgroundImage");
2072
    if (os == undefined){
2073
        new_img = current_img;
2074
    }
2075

    
2076
    // os changed
2077
    el.css("backgroundImage", new_img);
2078

    
2079
    // reset current state
2080
    if (skip_reset_states === undefined)
2081
    {
2082
        el.removeClass("single-image-state1");
2083
        el.removeClass("single-image-state2");
2084
        el.removeClass("single-image-state3");
2085
        el.removeClass("single-image-state4");
2086
    }
2087

    
2088
    if (remove_state !== undefined)
2089
    {
2090
        remove_state = "single-image-" + states_map[remove_state];
2091
        el.removeClass(remove_state);
2092
        return;
2093
    }
2094
    
2095
    // set proper state
2096
    el.addClass("single-image-" + cls);
2097
}
2098

    
2099

    
2100
// generic info box
2101

    
2102
function msg_box(config) {
2103
    var config = $.extend({'title':'Info message', 'content': 'this is an info message', 'ajax': false, 'extra':false}, config);
2104
    // prepare the error message
2105
    // bring up success notification
2106

    
2107
    var box = $("#notification-box");
2108
    box.addClass('success');
2109
    box.removeClass('error');
2110

    
2111
    var sel = function(s){return $(s, box)};
2112

    
2113
    sel("h3 span.header-box").text(config.title);
2114
    sel("div.machine-now-building").html(config.content);
2115
    sel(".popup-header").removeClass("popup-header-error");
2116
    box.removeClass("popup-border-error");
2117
    sel(".popup-details").removeClass("popup-details-error");
2118
    sel(".popup-separator").removeClass("popup-separator-error");
2119
    
2120
    sel(".password-container").hide();
2121
    if (config.extra) {
2122
        sel(".password-container .password").html(config.extra);
2123
        sel(".password-container").show();
2124
    }
2125

    
2126
    var triggers = $("a#msgbox").overlay({
2127
        // some mask tweaks suitable for modal dialogs
2128
        mask: '#666',
2129
        top: 'center',
2130
        closeOnClick: false,
2131
        oneInstance: false,
2132
        load: false,
2133
        onClose: function () {
2134
            // With partial refresh working properly,
2135
            // it is no longer necessary to refresh the whole page
2136
            // choose_view();
2137
        }
2138
    });
2139
    $("a#msgbox").data('overlay').load();
2140
    
2141
    var parse_data = config.parse_data || false;
2142
    var load_html = config.html || false;
2143
    var user_success = config.success || false;
2144
    config.ajax = config.ajax || {};
2145

    
2146
    // requested to show remote data in msg_box
2147
    if (config.ajax) {
2148
        $.ajax($.extend({ 
2149
            url:config.ajax, 
2150
            success: function(data){
2151
                // we want to get our data parsed before
2152
                // placing them in content
2153
                if (parse_data) {
2154
                    data = parse_data(data);
2155
                }
2156

    
2157
                // no json response
2158
                // load html body
2159
                if (load_html) {
2160
                    sel("div.machine-now-building").html(data);
2161
                    sel("div.machine-now-building").html(data);
2162
                } else {
2163

    
2164
                    if (data.title)
2165
                        sel("h3 span.header-box").text(data.title);
2166

    
2167
                    if (data.content) {
2168
                        sel("div.machine-now-building").html(data.content);
2169
                    }
2170
                    if (data.extra) {
2171
                        sel(".password-container .password").html(data.extra);
2172
                        sel(".password-container").show();
2173
                    }
2174
                }
2175

    
2176
                if (user_success) {
2177
                    user_success($("div.machine-now-building"));
2178
                }
2179
            }
2180
        }, config.ajax_config));
2181
    }
2182
    return false;
2183
}
2184

    
2185

    
2186
function show_invitations() {
2187

    
2188
    handle_invitations = function(el) {
2189
        el.addClass("invitations");
2190
        var cont = el;
2191
        var form = $(el).find("form");
2192

    
2193
        // remove garbage rows that stay in DOM between requests
2194
        $(".removable-field-row:hidden").remove();
2195
        $("#invform #removable-name-container-1").dynamicField();
2196
        form.submit(function(evn){
2197
            evn.preventDefault();
2198
            $.post(form.attr("action"), form.serialize(), function(data) {
2199
                $(cont).html(data); 
2200
                handle_invitations(cont);
2201
            });
2202
            return false;
2203
        });
2204
    }
2205
    msg_box({title:window.INVITATIONS_TITLE, content:'', ajax:INVITATIONS_URL, html:true, success: function(el){ 
2206
        handle_invitations(el)}
2207
    });
2208
}
2209