Statistics
| Branch: | Tag: | Revision:

root / ui / static / synnefo.js @ 67921b5c

History | View | Annotate | Download (78 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
Object.prototype.toString = function(o){
67
    
68
    var parse = function(_o){
69
        var a = [], t;
70
        for(var p in _o){
71
            if(_o.hasOwnProperty(p)){
72
                t = _o[p];
73
                if(t && typeof t == "object"){
74
                    a[a.length]= p + ":{ " + arguments.callee(t).join(", ") + "}";
75
                }
76
                else {
77
                    if(typeof t == "string"){
78
                        a[a.length] = [ p+ ": \"" + t.toString() + "\"" ];
79
                    }
80
                    else{
81
                        a[a.length] = [ p+ ": " + t.toString()];
82
                    }
83
                }
84
            }
85
        }
86
        return a;
87
        
88
    }
89
    return "{" + parse(o).join(", ") + "}";
90
   
91
}
92

    
93
// jquery show/hide events
94
var _oldshow = $.fn.show;
95
$.fn.show = function(speed, callback) {
96
    $(this).trigger('show');
97
    return _oldshow.apply(this,arguments);
98
}
99
var _oldhide = $.fn.hide;
100
$.fn.hide = function(speed, callback) {
101
    $(this).trigger('hide');
102
    return _oldhide.apply(this,arguments);
103
}
104

    
105
function ISODateString(d){
106
    //return a date in an ISO 8601 format using UTC.
107
    //do not include time zone info (Z) at the end
108
    //taken from the Mozilla Developer Center
109
    function pad(n){ return n<10 ? '0'+n : n }
110
    return  d.getUTCFullYear()+ '-' +
111
            pad(d.getUTCMonth()+1) + '-' +
112
            pad(d.getUTCDate()) + 'T' +
113
            pad(d.getUTCHours()) + ':' +
114
            pad(d.getUTCMinutes()) + ':' +
115
            pad(d.getUTCSeconds()) +'Z'
116
}
117

    
118
function parse_error(responseText, errorCode){
119
    var errors = [];
120
    try {
121
        responseObj = JSON.parse(responseText);
122
    }
123
    catch(err) {
124
        errors[0] = {'code': errorCode};
125
        return errors;
126
    }
127
    for (var err in responseObj){
128
        errors[errors.length] = responseObj[err];
129
    }
130
    return errors;
131
}
132

    
133
// indexOf prototype for IE
134
if (!Array.prototype.indexOf) {
135
  Array.prototype.indexOf = function(elt /*, from*/) {
136
    var len = this.length;
137
    var from = Number(arguments[1]) || 0;
138
    from = (from < 0)
139
         ? Math.ceil(from)
140
         : Math.floor(from);
141
    if (from < 0)
142
      from += len;
143

    
144
    for (; from < len; from++) {
145
      if (from in this &&
146
          this[from] === elt)
147
        return from;
148
    }
149
    return -1;
150
  };
151
}
152

    
153
// trim prototype for IE
154
if(typeof String.prototype.trim !== 'function') {
155
    String.prototype.trim = function() {
156
        return this.replace(/^\s+|\s+$/g, '');
157
    }
158
}
159

    
160
function update_confirmations() {
161
    // hide all confirm boxes to begin with
162
    $('#machines-pane div.confirm_single').hide();
163
    $('#machines-pane div.confirm_multiple').hide();
164
    var action_type = [];
165
    // standard view or single view
166
    if ($.cookie("view") == '0' || $.cookie("view") == '2') {
167
        for (var i=0; i<pending_actions.length; i++) {
168
            // show single confirms
169
            if (pending_actions[i][0] == reboot) {
170
                action_type = "reboot";
171
            } else if (pending_actions[i][0] == shutdown) {
172
                action_type = "shutdown";
173
            } else if (pending_actions[i][0] == start) {
174
                action_type = "start";
175
            } else if (pending_actions[i][0] == open_console) {
176
                action_type = "console";
177
            } else {
178
                action_type = "destroy";
179
            }
180
            $("#machines-pane #" + pending_actions[i][1] +
181
            " div.action-container." + action_type + " div.confirm_single").show();
182
        }
183
    }
184
    // if more than one pending action show multiple confirm box
185
    if (pending_actions.length>1 || $.cookie("view") == '1' && pending_actions.length == 1){
186
        $('#machines-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
187
        $('#machines-pane div.confirm_multiple').show();
188
    }
189
}
190

    
191
function update_network_confirmations(){
192
    // hide all confirm boxes to begin with
193
    $('#networks-pane div.confirm_multiple').hide();
194

    
195
    for (var i=0;i<pending_actions.length;i++){
196
        // show single confirms depending on the action
197
        if (pending_actions[i][0] == delete_network) {
198
            $("#networks-pane div.network#net-"+pending_actions[i][1]).children('.confirm_single').show();
199
        } else if (pending_actions[i][0] == remove_server_from_network) {
200
            $("#networks-pane div.network #net-"+pending_actions[i][1]+"-server-"+pending_actions[i][2]).children('.confirm_single').show();
201
        } // else {}
202
    }
203

    
204
    // if more than one pending action show multiple confirm box
205
    if (pending_actions.length > 1){
206
        $('#networks-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
207
        $('#networks-pane div.confirm_multiple').show();
208
    }
209
}
210

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

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

    
269
function standard_view() {
270
    changes_since = 0; // to reload full list
271
    pending_actions = []; // clear pending actions
272
    update_confirmations();
273
    clearTimeout(deferred);    // clear old deferred calls
274
    try {
275
        update_request.abort() // cancel pending ajax updates
276
        load_request.abort();
277
    }catch(err){}
278
    $.cookie("view", '0');
279
    uri = $("a#standard").attr("href");
280
    load_request = $.ajax({
281
        url: uri,
282
        type: "GET",
283
        timeout: TIMEOUT,
284
        dataType: "html",
285
        error: function(jqXHR, textStatus, errorThrown) {
286
            return false;
287
        },
288
        success: function(data, textStatus, jqXHR) {
289
            $("a#standard")[0].className += ' activelink';
290
            $("a#list")[0].className = '';
291
            $("a#single")[0].className = '';
292
            $("div#machinesview").html(data);
293
        }
294
    });
295
    return false;
296
}
297

    
298
function choose_view() {
299
    if ($.cookie("view")=='1') {
300
        list_view();
301
    } else if ($.cookie("view")=='2'){
302
        single_view();
303
    } else {
304
        standard_view();
305
    }
306
}
307

    
308
// return value from metadata key "OS", if it exists
309
function os_icon(metadata) {
310
    if (!metadata) {
311
        return 'okeanos';
312
    }
313
    if (metadata.values.OS == undefined || metadata.values.OS == '') {
314
        return 'okeanos';
315
    } else {
316
        if (os_icons.indexOf(metadata.values.OS) == -1) {
317
            return 'okeanos';
318
        } else {
319
            return metadata.values.OS;
320
        }
321
    }
322
}
323

    
324
function os_icon_from_value(metadata) {
325
    if (!metadata) {
326
        return 'okeanos';
327
    }
328
if (metadata == undefined || metadata == '') {
329
        return 'okeanos';
330
    } else {
331
        if (os_icons.indexOf(metadata) == -1) {
332
            return 'okeanos';
333
        } else {
334
            return metadata;
335
        }
336
    }
337
}
338

    
339
// get and show a list of running and terminated machines
340
function update_vms(interval) {
341
    try{ console.info('updating machines'); } catch(err){}
342
    var uri= API_URL + '/servers/detail';
343

    
344
    if (changes_since != 0)
345
        uri+='?changes-since='+changes_since
346

    
347
    update_request = $.ajax({
348
        cache: false,
349
        url: uri,
350
        type: "GET",
351
        timeout: TIMEOUT,
352
        dataType: "json",
353
        error: function(jqXHR, textStatus, errorThrown) {
354
            // don't forget to try again later
355
            if (interval) {
356
                clearTimeout(deferred);    // clear old deferred calls
357
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
358
            }
359
            // as for now, just show an error message
360
            try { console.info('update_vms errback:' + jqXHR.status ) } catch(err) {}
361
            try {
362
                ajax_error(jqXHR.status, undefined, 'Update VMs', jqXHR.responseText);
363
            } catch(err) {
364
                ajax_error(-5, "UI Error", 'Update VMs', err);
365
            }
366
            return false;
367
            },
368
        success: function(data, textStatus, jqXHR) {
369
            // create changes_since string if necessary
370
            if (jqXHR.getResponseHeader('Date') != null){
371
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
372
                changes_since = ISODateString(changes_since_date);
373
            }
374

    
375
            if (interval) {
376
                clearTimeout(deferred);    // clear old deferred calls
377
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
378
            }
379

    
380
            if (jqXHR.status == 200 || jqXHR.status == 203) {
381
                try {
382
                    //servers = data.servers.values;
383
                    update_servers_data(data.servers.values, data);
384
                    update_machines_view(data);
385
                } catch(err) { ajax_error(-5, "UI Error", 'Update VMs', err);}
386
            } else if (jqXHR.status != 304){
387
                try { console.info('update_vms callback:' + jqXHR.status ) } catch(err) {}
388
                /*
389
                FIXME:  Here it should return the error, however Opera does not support 304.
390
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
391
                        304, which should be corrected (Bug #317).
392
                */
393
                // ajax_error(jqXHR.status, "Ajax error", 'Update VMs', jqXHR.responseText);
394
            }
395
            return false;
396
        }
397
    });
398
    return false;
399
}
400

    
401
function update_servers_data(servers_update, data) {
402
    $(window).trigger("vm:update", servers_update, data);
403

    
404
    // first call
405
    if (!window.servers || window.servers.length == 0) {
406
        window.servers = servers_update;
407
        return;
408
    }
409
    
410
    // server exists helper
411
    server_exists = function(server) {
412
        var id = server.id;
413
        var found = false;
414
        var index = 0;
415
        $.each(servers, function(i, s) {
416
            if (s.id == id) { found = true, index = i };
417
        });
418
        if (found)
419
            return [found, index];
420

    
421
        return false;
422
    }
423

    
424
    // merge object properties
425
    merge = function() {
426
        var initial = arguments[0];
427
        var status_changed = undefined;
428
        $.each(arguments, function(index, el) {
429
            $.each(el, function(key,v) {
430
                // new attribute added
431
                var previous_value = initial[key];
432
                var v = v;
433
                if (initial[key] == undefined) {
434
                    $(window).trigger("vm:attr:add", initial, key, v);
435
                } else {
436
                    // value changed
437
                    if (initial[key] != v) {
438
                        if (key == "status") {
439
                            // dont change if in destroy state
440
                            if (initial.status == "DESTROY") {
441
                                v = "DESTROY";
442
                            }
443
                            status_changed = {'old': previous_value, 'new': v}; 
444
                        }
445
                        $(window).trigger("vm:attr:change", {'initial': initial, 'attr': key, 'newvalue': v});
446
                    }
447
                }
448
                initial[key] = v;
449
            });
450
        });
451
        if (status_changed !== undefined) {
452
            $(window).trigger('vm:status:change', {'vm': initial, 'old': status_changed['old'], 'new': status_changed['new']});
453
        }
454
        return initial;
455
    }
456
    
457
    // server removed
458
    var remove = [];
459
    $.each(servers_update, function(index, server) {
460
        if (server.status == "DELETED") {
461
            remove.push(server.id);
462
        }
463
    });
464
    
465
    // check server, if exists merge it with new values else add it
466
    $.each(servers_update, function(index, server) {
467
        var exists = server_exists(server);
468
        if (exists !== false) {
469
            try {
470
                servers[exists[1]] = merge(servers[exists[1]], server);
471
            } catch (err) {
472
            }
473
        } else {
474
            servers.push(server);
475
            $(window).trigger("vm:add", server);
476
        }
477
        if (remove.indexOf(server.id) > -1) {
478
            var remove_exists = server_exists(server);
479
            servers.splice(remove_exists[1], 1);
480
            $(window).trigger("vm:remove", server);
481
        }
482
    });
483
}
484

    
485
// get a list of running and terminated machines, used in network view
486
function update_networks(interval) {
487
    try{ console.info('updating networks'); } catch(err){}
488
    var uri= API_URL + '/servers/detail';
489

    
490
    if (changes_since != 0)
491
        uri+='?changes-since='+changes_since
492

    
493
    update_request = $.ajax({
494
        cache: false,
495
        url: uri,
496
        type: "GET",
497
        timeout: TIMEOUT,
498
        dataType: "json",
499
        error: function(jqXHR, textStatus, errorThrown) {
500
            // don't forget to try again later
501
            if (interval) {
502
                clearTimeout(deferred);    // clear old deferred calls
503
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
504
            }
505
            // as for now, just show an error message
506
            try { console.info('update_networks errback:' + jqXHR.status ) } catch(err) {}
507
            try {
508
                ajax_error(jqXHR.status, undefined, 'Update networks', jqXHR.responseText);
509
            } catch(err) {
510
                ajax_error(-5, "UI Error", 'Update networks', err);
511
            }
512
            return false;
513
            },
514
        success: function(data, textStatus, jqXHR) {
515
            // create changes_since string if necessary
516
            if (jqXHR.getResponseHeader('Date') != null){
517
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
518
                changes_since = ISODateString(changes_since_date);
519
            }
520

    
521
            if (interval) {
522
                clearTimeout(deferred);    // clear old deferred calls
523
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
524
            }
525

    
526
            if (jqXHR.status == 200 || jqXHR.status == 203) {
527
                try {
528
                    //servers = data.servers.values;
529
                    update_servers_data(data.servers.values, data);
530
                    jQuery.parseJSON(data);
531
                    update_network_names(data);
532
                } catch(err) { ajax_error(-5, "UI Error", 'Update networks', err);}
533
            } else if (jqXHR.status == 304) {
534
                update_network_names();
535
            }
536
            else {
537
                try { console.info('update_networks callback:' + jqXHR.status ) } catch(err) {}
538
                /*
539
                FIXME:  Here it should return the error, however Opera does not support 304.
540
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
541
                        304, which should be corrected (Bug #317).
542
                */
543
                //ajax_error(jqXHR.status, undefined, 'Update networks', jqXHR.responseText);
544
                update_network_names();
545
            }
546
            return false;
547
        }
548
    });
549
    return false;
550
}
551

    
552
// get and show a list of public and private networks
553
function update_network_names(servers_data) {
554
    try{ console.info('updating network names'); } catch(err){}
555
    var uri= API_URL + '/networks/detail';
556

    
557
    if (networks_changes_since != 0)
558
        //FIXME: Comment out the following, until metadata do not 304 when changed
559
        uri+='?changes-since=' + networks_changes_since
560

    
561
    update_request = $.ajax({
562
        cache: false,
563
        url: uri,
564
        type: "GET",
565
        timeout: TIMEOUT,
566
        dataType: "json",
567
        error: function(jqXHR, textStatus, errorThrown) {
568
            // as for now, just show an error message
569
            try {
570
                console.info('update_network names errback:' + jqXHR.status )
571
            } catch(err) {}
572
            try {
573
                ajax_error(jqXHR.status, undefined, 'Update network names', jqXHR.responseText);
574
            } catch(err) {
575
                ajax_error(-5, "UI Error", 'Update network names', err);
576
            }
577
            return false;
578
            },
579
        success: function(data, textStatus, jqXHR) {
580
            // create changes_since string if necessary
581
            if (jqXHR.getResponseHeader('Date') != null){
582
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
583
                networks_changes_since = ISODateString(changes_since_date);
584
            }
585

    
586
            if (jqXHR.status == 200 || jqXHR.status == 203) {
587
                try {
588
                    networks = data.networks.values;
589
                    jQuery.parseJSON(data);
590
                    update_networks_view(servers_data, data);
591
                } catch(err) {
592
                    ajax_error(-5, "UI Error", 'Update network names', err);
593
                }
594
            } else if (jqXHR.status == 304) {
595
                update_networks_view(servers_data);
596
            } else if (jqXHR.status != 304){
597
                try { console.info('update_network_names callback:' + jqXHR.status ) } catch(err) {}
598
                /*
599
                FIXME:  Here it should return the error, however Opera does not support 304.
600
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
601
                        304, which should be corrected (Bug #317).
602
                */
603
                //ajax_error(jqXHR.status, undefined, 'Update network names', jqXHR.responseText);
604
                update_networks_view(servers_data);
605
            }
606
            return false;
607
        }
608
    });
609
    return false;
610
}
611

    
612
// get and show a list of available standard and custom images
613
function update_images() {
614
    $.ajax({
615
        url: API_URL + '/images/detail',
616
        type: "GET",
617
        //async: false,
618
        dataType: "json",
619
        timeout: TIMEOUT,
620
        error: function(jqXHR, textStatus, errorThrown) {
621
                    try {
622
                        ajax_error(jqXHR.status, undefined, 'Update Images', jqXHR.responseText);
623
                    } catch(err) {
624
                        ajax_error(-5, "UI error", 'Update Images', err);
625
                    }
626
                },
627
        success: function(data, textStatus, jqXHR) {
628
            try {
629
                images = data.images.values;
630
                jQuery.parseJSON(data);
631
                update_wizard_images();
632
            } catch(err){
633
                ajax_error("NO_IMAGES");
634
            }
635
        }
636
    });
637
    return false;
638
}
639

    
640
function update_wizard_images() {
641
    if ($("ul#standard-images li").toArray().length + $("ul#custom-images li").toArray().length == 0) {
642
        $.each(images, function(i,image){
643
            var img = $('#image-template').clone().attr("id","img-"+image.id).fadeIn("slow");
644
            img.find("label").attr('for',"img-radio-" + image.id);
645
            img.find(".image-title").text(image.name);
646
            if (image.metadata) {
647
                if (image.metadata.values.description != undefined) {
648
                    img.find(".description").text(image.metadata.values.description);
649
                }
650
                if (image.metadata.values.size != undefined) {
651
                    img.find("#size").text(image.metadata.values.size);
652
                }
653
            }
654
            img.find("input.radio").attr('id',"img-radio-" + image.id);
655
            if (i==0) img.find("input.radio").attr("checked","checked");
656
            var image_logo = os_icon(image.metadata);
657
            img.find("img.image-logo").attr('src','static/icons/os/'+image_logo+'.png');
658
            if (image.metadata) {
659
                if (image.metadata.values.serverId != undefined) {
660
                    img.appendTo("ul#custom-images");
661
                } else {
662
                    img.appendTo("ul#standard-images");
663
                }
664
            } else {
665
                img.appendTo("ul#standard-images");
666
            }
667
        });
668
    }
669
}
670

    
671
function update_wizard_flavors(){
672
    // sliders for selecting VM flavor
673
    $("#cpu:range").rangeinput({min:0,
674
                               value:0,
675
                               step:1,
676
                               progress: true,
677
                               max:cpus.length-1});
678

    
679
    $("#storage:range").rangeinput({min:0,
680
                               value:0,
681
                               step:1,
682
                               progress: true,
683
                               max:disks.length-1});
684

    
685
    $("#ram:range").rangeinput({min:0,
686
                               value:0,
687
                               step:1,
688
                               progress: true,
689
                               max:ram.length-1});
690
    $("#small").click();
691

    
692
    // update the indicators when sliding
693
    $("#cpu:range").data().rangeinput.onSlide(function(event,value){
694
        $("#cpu-indicator")[0].value = cpus[Number(value)];
695
        $("#cpu-indicator").addClass('selectedrange');
696
    });
697
    $("#cpu:range").data().rangeinput.change(function(event,value){
698
        $("#cpu-indicator")[0].value = cpus[Number(value)];
699
        $("#custom").click();
700
        $("#cpu-indicator").removeClass('selectedrange');
701
    });
702
    $("#ram:range").data().rangeinput.onSlide(function(event,value){
703
        $("#ram-indicator")[0].value = ram[Number(value)];
704
        $("#ram-indicator").addClass('selectedrange');
705
    });
706
    $("#ram:range").data().rangeinput.change(function(event,value){
707
        $("#ram-indicator")[0].value = ram[Number(value)];
708
        $("#custom").click();
709
        $("#ram-indicator").removeClass('selectedrange');
710
    });
711
    $("#storage:range").data().rangeinput.onSlide(function(event,value){
712
        $("#storage-indicator")[0].value = disks[Number(value)];
713
        $("#storage-indicator").addClass('selectedrange');
714
    });
715
    $("#storage:range").data().rangeinput.change(function(event,value){
716
        $("#storage-indicator")[0].value = disks[Number(value)];
717
        $("#custom").click();
718
        $("#storage-indicator").removeClass('selectedrange');
719
    });
720
}
721

    
722
Array.prototype.unique = function () {
723
    var r = new Array();
724
    o:for(var i = 0, n = this.length; i < n; i++)
725
    {
726
        for(var x = 0, y = r.length; x < y; x++)
727
        {
728
            if(r[x]==this[i])
729
            {
730
                continue o;
731
            }
732
        }
733
        r[r.length] = this[i];
734
    }
735
    return r;
736
}
737

    
738
// get and configure flavor selection
739
function update_flavors() {
740
    $.ajax({
741
        url: API_URL + '/flavors/detail',
742
        type: "GET",
743
        //async: false,
744
        dataType: "json",
745
        timeout: TIMEOUT,
746
        error: function(jqXHR, textStatus, errorThrown) {
747
            try {
748
                ajax_error(jqXHR.status, undefined, 'Update Flavors', jqXHR.responseText);
749
            } catch (err) {
750
                ajax_error(-5, "UI Error", "Update Flavors", err);
751
            }
752
            // start updating vm list
753
            update_vms(UPDATE_INTERVAL);
754
        },
755
        success: function(data, textStatus, jqXHR) {
756

    
757
            try {
758
                flavors = data.flavors.values;
759
                jQuery.parseJSON(data);
760
                $.each(flavors, function(i, flavor) {
761
                    cpus[i] = flavor['cpu'];
762
                    disks[i] = flavor['disk'];
763
                    ram[i] = flavor['ram'];
764
                });
765
                cpus = cpus.unique();
766
                disks = disks.unique();
767
                ram = ram.unique();
768
                update_wizard_flavors();
769
            } catch(err){
770
                ajax_error("NO_FLAVORS");
771
            }
772
            // start updating vm list
773
            update_vms(UPDATE_INTERVAL);
774
        }
775
    });
776
    return false;
777
}
778

    
779
// return flavorRef from cpu, disk, ram values
780
function identify_flavor(cpu, disk, ram){
781
    for (i=0;i<flavors.length;i++){
782
        if (flavors[i]['cpu'] == cpu && flavors[i]['disk']==disk && flavors[i]['ram']==ram) {
783
            return flavors[i]['id']
784
        }
785
    }
786
    return 0;
787
}
788

    
789
// return image entry from imageRef
790
function get_image(imageRef) {
791
    for (i=0;i<images.length;i++){
792
        if (images[i]['id'] == imageRef) {
793
            return images[i];
794
        }
795
    }
796
    return 0;
797
}
798

    
799
// return machine entry from serverID
800
function get_machine(serverID) {
801
    for (i=0;i<servers.length;i++){
802
        if (servers[i]['id'] == serverID) {
803
            return servers[i];
804
        }
805
    }
806
    return 0;
807
}
808

    
809
// update the actions in icon view, per server
810
function update_iconview_actions(serverID, server_status) {
811
    if ($.cookie("view")=='2') {
812
        // remove .disable from all actions to begin with
813
        $('#machinesview-single #' + serverID + ' div.single-action').show();
814
        // decide which actions should be disabled
815
        for (current_action in actions) {
816
            if (actions[current_action].indexOf(server_status) == -1 ) {
817
                $('#machinesview-single #' + serverID + ' div.action-' + current_action).hide();
818
            }
819
        }
820
    } else {
821
        // remove .disable from all actions to begin with
822
        $('#machinesview-icon.standard #' + serverID + ' div.actions').find('a').removeClass('disabled');
823
        // decide which actions should be disabled
824
        for (current_action in actions) {
825
            if (actions[current_action].indexOf(server_status) == -1 ) {
826
                $('#machinesview-icon.standard #' + serverID + ' a.action-' + current_action).addClass('disabled');
827
            }
828
        }
829
    }
830
}
831

    
832
// update the actions in list view
833
function update_listview_actions() {
834
    var states = [];
835
    var on = [];
836
    var checked = $("table.list-machines tbody input[type='checkbox']:checked");
837
    // disable all actions to begin with
838
    $('#machinesview .list div.actions').children().removeClass('enabled');
839

    
840
    // are there multiple machines selected?
841
    if (checked.length>1)
842
        states[0] = 'multiple';
843

    
844
    // check the states of selected machines
845
    checked.each(function(i,checkbox) {
846
        states[states.length] = checkbox.className;
847
        var ip = $("#" + checkbox.id.replace('input-','') + ".ip span.public").text();
848
        if (ip.replace('undefined','').length)
849
            states[states.length] = 'network';
850
    });
851

    
852
    // decide which actions should be enabled
853
    for (a in actions) {
854
        var enabled = false;
855
        for (var s =0; s<states.length; s++) {
856
            if (actions[a].indexOf(states[s]) != -1 ) {
857
                enabled = true;
858
            } else {
859
                enabled = false;
860
                break;
861
            }
862
        }
863
        if (enabled)
864
            on[on.length]=a;
865
    }
866
    // enable those actions
867
    for (action in on) {
868
        $("#action-" + on[action]).addClass('enabled');
869
    }
870
}
871

    
872
//create server action
873
function create_vm(machineName, imageRef, flavorRef){
874
    var image_logo = os_icon(get_image(imageRef).metadata);
875
    var uri = API_URL + '/servers';
876
    var payload = {
877
        "server": {
878
            "name": machineName,
879
            "imageRef": imageRef,
880
            "flavorRef" : flavorRef,
881
            "metadata" : {
882
                "OS" : image_logo
883
            }
884
        }
885
    };
886

    
887
    $.ajax({
888
    url: uri,
889
    type: "POST",
890
    contentType: "application/json",
891
    dataType: "json",
892
    data: JSON.stringify(payload),
893
    timeout: TIMEOUT,
894
    error: function(jqXHR, textStatus, errorThrown) {
895
                // close wizard and show error box
896
                $('#machines-pane a#create').data('overlay').close();
897
                    try {
898
                        ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
899
                    } catch(err) {
900
                        ajax_error(-5, "UI Error", 'Create VM', err);
901
                    }
902
           },
903
    success: function(data, textStatus, jqXHR) {
904
                if ( jqXHR.status == '202') {
905
                    ajax_success("CREATE_VM_SUCCESS", data.server.adminPass);
906
                } else {
907
                    // close wizard and show error box
908
                    $('#machines-pane a#create').data('overlay').close();
909
                    ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
910
                }
911
            }
912
    });
913
}
914

    
915
// reboot action
916
function reboot(serverIDs){
917
    if (!serverIDs.length){
918
        //ajax_success('DEFAULT');
919
        return false;
920
    }
921
    // ajax post reboot call
922
    var payload = {
923
        "reboot": {"type" : "HARD"}
924
    };
925

    
926
    var serverID = serverIDs.pop();
927

    
928
    $.ajax({
929
        url: API_URL + '/servers/' + serverID + '/action',
930
        type: "POST",
931
        contentType: "application/json",
932
        dataType: "json",
933
        data: JSON.stringify(payload),
934
        timeout: TIMEOUT,
935
        error: function(jqXHR, textStatus, errorThrown) {
936
                    // in machine views
937
                    if ( $.cookie("pane") == 0) {
938
                        try {
939
                            display_failure(jqXHR.status, serverID, 'Reboot', jqXHR.responseText);
940
                        } catch (err) {
941
                            display_failure(0, serverID, 'Reboot', jqXHR.responseText);
942
                        }
943
                    }
944
                    // in network view
945
                    else {
946
                        try {
947
                            display_reboot_failure(jqXHR.status, serverID, jqXHR.responseText);
948
                        } catch (err) {
949
                            display_reboot_failure(0, serverID, jqXHR.responseText);
950
                        }
951
                    }
952
                },
953
        success: function(data, textStatus, jqXHR) {
954
                    if ( jqXHR.status == '202') {
955
                        try {
956
                            console.info('rebooted ' + serverID);
957
                        } catch(err) {}
958
                        // indicate that the action succeeded
959
                        // in machine views
960
                        if ( $.cookie("pane") == 0) {
961
                            display_success(serverID);
962
                        }
963
                        // in network view
964
                        else {
965
                            display_reboot_success(serverID);
966
                        }
967
                        // continue with the rest of the servers
968
                        reboot(serverIDs);
969
                    } else {
970
                        ajax_error(jqXHR.status, serverID, 'Reboot', jqXHR.responseText);
971
                    }
972
                }
973
    });
974
    return false;
975
}
976

    
977
// shutdown action
978
function shutdown(serverIDs) {
979
    if (!serverIDs.length){
980
        //ajax_success('DEFAULT');
981
        return false;
982
    }
983
    // ajax post shutdown call
984
    var payload = {
985
        "shutdown": {}
986
    };
987

    
988
    var serverID = serverIDs.pop();
989

    
990
    $.ajax({
991
        url: API_URL + '/servers/' + serverID + '/action',
992
        type: "POST",
993
        contentType: "application/json",
994
        dataType: "json",
995
        data: JSON.stringify(payload),
996
        timeout: TIMEOUT,
997
        error: function(jqXHR, textStatus, errorThrown) {
998
                    try {
999
                        display_failure(jqXHR.status, serverID, 'Shutdown', jqXHR.responseText);
1000
                    } catch(err) {
1001
                        display_failure(0, serverID, 'Shutdown', jqXHR.responseText);
1002
                    }
1003
                },
1004
        success: function(data, textStatus, jqXHR) {
1005
                    if ( jqXHR.status == '202') {
1006
                        try {
1007
                            console.info('suspended ' + serverID);
1008
                        } catch(err) {}
1009
                        // indicate that the action succeeded
1010
                        display_success(serverID);
1011
                        // continue with the rest of the servers
1012
                        shutdown(serverIDs);
1013
                    } else {
1014
                        ajax_error(jqXHR.status, serverID, 'Shutdown', jqXHR.responseText);
1015
                    }
1016
                }
1017
    });
1018
    return false;
1019
}
1020

    
1021
// destroy action
1022
function destroy(serverIDs) {
1023
    if (!serverIDs.length){
1024
        //ajax_success('DEFAULT');
1025
        return false;
1026
    }
1027
    // ajax post destroy call can have an empty request body
1028
    var payload = {};
1029

    
1030
    var serverID = serverIDs.pop();
1031

    
1032
    $.ajax({
1033
        url: API_URL + '/servers/' + serverID,
1034
        type: "DELETE",
1035
        contentType: "application/json",
1036
        dataType: "json",
1037
        data: JSON.stringify(payload),
1038
        timeout: TIMEOUT,
1039
        error: function(jqXHR, textStatus, errorThrown) {
1040
                    try {
1041
                        display_failure(jqXHR.status, serverID, 'Destroy', jqXHR.responseText);
1042
                    } catch(err) {
1043
                        display_failure(0, serverID, 'Destroy', jqXHR.responseText);
1044
                    }
1045
                },
1046
        success: function(data, textStatus, jqXHR) {
1047
                    if ( jqXHR.status == '204') {
1048
                        try {
1049
                            console.info('destroyed ' + serverID);
1050
                        } catch (err) {}
1051

    
1052
                        // update status on local storage object
1053
                        vm = get_machine(serverID);
1054
                        vm.status = "DESTROY";
1055

    
1056
                        // indicate that the action succeeded
1057
                        display_success(serverID);
1058
                        // continue with the rest of the servers
1059
                        destroy(serverIDs);
1060
                    } else {
1061
                        ajax_error(jqXHR.status, serverID, 'Destroy', jqXHR.responseText);
1062
                    }
1063
                }
1064
    });
1065
    return false;
1066
}
1067

    
1068
// start action
1069
function start(serverIDs){
1070
    if (!serverIDs.length){
1071
        //ajax_success('DEFAULT');
1072
        return false;
1073
    }
1074
    // ajax post start call
1075
    var payload = {
1076
        "start": {}
1077
    };
1078

    
1079
    var serverID = serverIDs.pop();
1080

    
1081
    $.ajax({
1082
        url: API_URL + '/servers/' + serverID + '/action',
1083
        type: "POST",
1084
        contentType: "application/json",
1085
        dataType: "json",
1086
        data: JSON.stringify(payload),
1087
        timeout: TIMEOUT,
1088
        error: function(jqXHR, textStatus, errorThrown) {
1089
                    try {
1090
                        display_failure(jqXHR.status, serverID, 'Start', jqXHR.responseText);
1091
                    } catch(err) {
1092
                        display_failure(0, serverID, 'Start', jqXHR.responseText);
1093
                    }
1094
                },
1095
        success: function(data, textStatus, jqXHR) {
1096
                    if ( jqXHR.status == '202') {
1097
                        try {
1098
                            console.info('started ' + serverID);
1099
                        } catch(err) {}
1100
                        // indicate that the action succeeded
1101
                        display_success(serverID);
1102
                        // continue with the rest of the servers
1103
                        start(serverIDs);
1104
                    } else {
1105
                        ajax_error(jqXHR.status, serverID, 'Start', jqXHR.responseText);
1106
                    }
1107
                }
1108
    });
1109
    return false;
1110
}
1111

    
1112
// Show VNC console
1113
function vnc_attachment(host, port, password) {
1114
    // FIXME: Must be made into parameters, in settings.py
1115
    //vnc = open("", "displayWindow",
1116
    //    "status=yes,toolbar=yes,menubar=yes");
1117
    vd = document.open("application/x-vnc");
1118

    
1119
    vd.writeln("[connection]");
1120
    vd.writeln("host=" + host);
1121
    vd.writeln("port=" + port);
1122
    vd.writeln("password=" + password);
1123

    
1124
    vd.close();
1125
}
1126

    
1127
// Show VNC console
1128
function show_vnc_console(serverID, serverName, serverIP, host, port, password) {
1129
    var params_url = '?machine=' + serverName + '&host_ip=' + serverIP + '&host=' + host + '&port=' + port + '&password=' + password;
1130
    var params_window = 'scrollbars=no,' +
1131
                        'menubar=no,' +
1132
                        'toolbar=no,' +
1133
                        'status=no,' +
1134
                        'top=0,' +
1135
                        'left=0,' +
1136
                        'height=' + screen.height + ',' +
1137
                        'width=' + screen.width + ',' +
1138
                        'fullscreen=yes';
1139

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

    
1142
    // Restore os icon in list view
1143
    osIcon = $('#'+serverID).parent().parent().find('.list-logo');
1144
    osIcon.attr('src',osIcon.attr('os'));
1145
    return false;
1146
}
1147

    
1148
// console action
1149
function open_console(serverIDs){
1150
    if (!serverIDs.length){
1151
        //ajax_success('DEFAULT');
1152
        return false;
1153
    }
1154
    // ajax post start call
1155
    var payload = {
1156
        "console": {"type": "vnc"}
1157
    };
1158

    
1159
    var serverID = serverIDs.pop();
1160

    
1161
    var machine = get_machine(serverID);
1162
    var serverName = machine.name;
1163
    try {
1164
        var serverIP = machine.addresses.values[0].values[0].addr;
1165
    } catch(err) { var serverIP = 'undefined'; }
1166

    
1167
    $.ajax({
1168
        url: API_URL + '/servers/' + serverID + '/action',
1169
        type: "POST",
1170
        contentType: "application/json",
1171
        dataType: "json",
1172
        data: JSON.stringify(payload),
1173
        timeout: TIMEOUT,
1174
        error: function(jqXHR, textStatus, errorThrown) {
1175
                    try {
1176
                        display_failure(jqXHR.status, serverID, 'Console', jqXHR.responseText);
1177
                    } catch(err) {
1178
                        display_failure(0, serverID, 'Console', jqXHR.responseText);
1179
                    }
1180
                },
1181
        success: function(data, textStatus, jqXHR) {
1182
                    if ( jqXHR.status == '200') {
1183
                        try {
1184
                            console.info('got_console ' + serverID);
1185
                        } catch(err) {}
1186
                        // indicate that the action succeeded
1187
                        // show_vnc_console(serverID, serverName, serverIP,
1188
                        // data.console.host,data.console.port,data.console.password);
1189
                        show_vnc_console(serverID, serverName, serverIP,
1190
                                         data.console.host,data.console.port,data.console.password);
1191
                        display_success(serverID);
1192
                        // hide spinner
1193
                        $('#' + serverID + ' .spinner').hide();
1194
                        // continue with the rest of the servers
1195
                        open_console(serverIDs);
1196
                    } else {
1197
                        ajax_error(jqXHR.status, serverID, 'Console', jqXHR.responseText);
1198
                    }
1199
                }
1200
    });
1201
    return false;
1202
}
1203

    
1204
function vm_has_address(vmId) {
1205
    var vm = get_machine(vmId);
1206

    
1207
    if (!vm) return false;
1208

    
1209
    try {
1210
        var ip = vm.addresses.values[0].values[0].addr;
1211
    } catch (err) {
1212
        return false;
1213
    }
1214
    return ip;
1215
}
1216

    
1217
// connect to machine action
1218
function machine_connect(serverIDs){
1219
    if (!serverIDs.length){
1220
        //ajax_success('DEFAULT');
1221
        return false;
1222
    }
1223

    
1224
    var serverID = serverIDs.pop();
1225
    var machine = get_machine(serverID);
1226
    var serverName = machine.name;
1227
    
1228
    try {
1229
        var serverIP = machine.addresses.values[0].values[0].addr;
1230
    } catch(err) { var serverIP = 'undefined'; }
1231

    
1232
    try {
1233
        var os = os_icon(machine.metadata);
1234
    } catch(err) { var os = 'undefined'; }
1235

    
1236
    var params_url = '?ip_address=' + serverIP + '&os=' + os + "&host_os=" + $.client.os + "&srv=" + serverID;
1237
    
1238
    if ($.client.os == "Windows" && os == "windows") {
1239
        window.open('machines/connect' + params_url + "&rdp=1");
1240
        return;
1241
    }
1242
    
1243
    var title = 'Connect to: ' + '<span class="machine-title"><img src="static/icons/machines/small/'+os+'-on.png" /> '+serverName+'</span>';
1244

    
1245
    try {
1246
        msg_box({title:title, content:'loading...',extra:'',
1247
        'ajax':'machines/connect' + params_url,
1248
        parse_data:function(data){
1249
            var box_content = "<a href='"+data.link.url+"'>"+data.link.title+"</a>";
1250
            if (!data.link.url) {
1251
                box_content = "<span class='cmd'>"+data.link.title+"</span>";
1252
            }
1253
            data.title = false;
1254
            data.content = data.info;
1255
            data.extra = box_content;
1256
            return data;
1257
        }
1258
        });
1259
    } catch (error) {
1260
        window.open('machines/connect' + params_url);
1261
    }
1262

    
1263

    
1264
    // Restore os icon in list view
1265
    osIcon = $('#'+serverID).parent().parent().find('.list-logo');
1266
    osIcon.attr('src',osIcon.attr('os'));
1267

    
1268
    return false;
1269
}
1270

    
1271

    
1272
// rename server
1273
function rename(serverID, serverName){
1274
    if (!serverID.length){
1275
        //ajax_success('DEFAULT');
1276
        return false;
1277
    }
1278
    // ajax post rename call
1279
    var payload = {
1280
        "server": {"name": serverName}
1281
    };
1282

    
1283
    $.ajax({
1284
        url: API_URL + '/servers/' + serverID,
1285
        type: "PUT",
1286
        contentType: "application/json",
1287
        dataType: "json",
1288
        data: JSON.stringify(payload),
1289
        timeout: TIMEOUT,
1290
        error: function(jqXHR, textStatus, errorThrown) {
1291
                    try {
1292
                        display_failure(jqXHR.status, serverID, 'Rename', jqXHR.responseText);
1293
                    } catch(err) {
1294
                        display_failure(0, serverID, 'Rename', jqXHR.responseText);
1295
                    }
1296
                },
1297
        success: function(data, textStatus, jqXHR) {
1298
                    if ( jqXHR.status == '204' || jqXHR.status == '1223') {
1299
                        try {
1300
                            console.info('renamed ' + serverID);
1301
                        } catch(err) {}
1302
                        // indicate that the action succeeded
1303
                        display_success(serverID);
1304
                    } else {
1305
                        ajax_error(jqXHR.status, serverID, 'Rename', jqXHR.responseText);
1306
                    }
1307
                }
1308
    });
1309
    return false;
1310
}
1311

    
1312
// get server metadata
1313
function get_metadata(serverID, keys_only) {
1314
    $.ajax({
1315
        url: API_URL + '/servers/' + serverID + '/meta',
1316
        cache: false,
1317
        type: "GET",
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, 'Get metadata', jqXHR.responseText);
1326
            } catch (err) {
1327
                ajax_error(-5, "UI Error", "Get metadata", err);
1328
            }
1329
        },
1330
        success: function(data, textStatus, jqXHR) {
1331
            // to list the new results in the edit dialog
1332
            if (keys_only) {
1333
                list_metadata_keys(serverID, data.metadata.values);
1334
            } else {
1335
                list_metadata(data);
1336
                list_metadata_keys(serverID, data.metadata.values);
1337
            }
1338
            //hide spinner
1339
            $('#metadata-wizard .large-spinner').hide();
1340
        }
1341
    });
1342
    return false;
1343
}
1344

    
1345
// delete metadata key-value pair
1346
function delete_metadata(serverID, meta_key) {
1347
    $.ajax({
1348
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1349
        type: "DELETE",
1350
        //async: false,
1351
        dataType: "json",
1352
        timeout: TIMEOUT,
1353
        error: function(jqXHR, textStatus, errorThrown) {
1354
            try {
1355
                // close wizard and show error box
1356
                $("a#metadata-scrollable").data('overlay').close();
1357
                ajax_error(jqXHR.status, undefined, 'Delete metadata', jqXHR.responseText);
1358
            } catch (err) {
1359
                ajax_error(-5, "UI Error", "Delete metadata", err);
1360
            }
1361
        },
1362
        success: function(data, textStatus, jqXHR) {
1363
                    // success: Do nothing, the UI is already updated
1364
        }
1365
    });
1366
    return false;
1367
}
1368

    
1369
// add metadata key-value pair
1370
function update_metadata(serverID, meta_key, meta_value) {
1371
    var payload = {
1372
        "meta": {
1373
        }
1374
    };
1375
    payload["meta"][meta_key] = meta_value;
1376

    
1377
    $.ajax({
1378
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1379
        type: "PUT",
1380
        contentType: "application/json",
1381
        dataType: "json",
1382
        data: JSON.stringify(payload),
1383
        timeout: TIMEOUT,
1384
        error: function(jqXHR, textStatus, errorThrown) {
1385
            try {
1386
                // close wizard and show error box
1387
                $("a#metadata-scrollable").data('overlay').close();
1388
                ajax_error(jqXHR.status, undefined, 'add metadata', jqXHR.responseText);
1389
            } catch (err) {
1390
                ajax_error(-5, "UI Error", "add metadata", err);
1391
            }
1392
        },
1393
        success: function(data, textStatus, jqXHR) {
1394
            // success: Update icons if meta key is OS
1395
            if (meta_key == "OS") {
1396
                $("#metadata-wizard .machine-icon").attr("src","static/icons/machines/small/" + os_icon_from_value(meta_value) + '-' + $("#metadata-wizard div#on-off").text() + '.png');
1397
                var machine_icon = $("#machinesview-icon").find("div#" + serverID);
1398
                var machine_single = $("#machinesview-single").find("div#" + serverID);
1399

    
1400
                var os = os_icon_from_value(meta_value);
1401
                var state = $("#metadata-wizard div#on-off").text()
1402
                var state_single = $(".state", machine_single).hasClass("terminated-state") ? "off" : "on";
1403
                set_machine_os_image(machine_icon, "icon", state, os);
1404
                set_machine_os_image(machine_single, "single", state_single, os);
1405
            }
1406
        }
1407
    });
1408
    return false;
1409
}
1410

    
1411
// get stats
1412
function get_server_stats(serverID) {
1413
    $.ajax({
1414
        url: API_URL + '/servers/' + serverID + '/stats',
1415
        cache: false,
1416
        type: "GET",
1417
        //async: false,
1418
        dataType: "json",
1419
        timeout: TIMEOUT,
1420
        error: function(jqXHR, textStatus, errorThrown) {
1421
                // report error as text inline
1422
                $('#' + serverID + ' img.busy').hide();
1423
                $('#' + serverID + ' div.stat-error').show();
1424
        },
1425
        success: function(data, textStatus, jqXHR) {
1426
            // in icon view
1427
            if ( $.cookie('view') == 0 ) {
1428
                $('#' + serverID + ' img.busy').removeClass('busy');
1429
                $('#' + serverID + ' img.cpu').attr("src", data.stats.cpuBar);
1430
                $('#' + serverID + ' img.net').attr("src", data.stats.netBar);
1431
            }
1432
            // in single view
1433
            else if ( $.cookie('view') == 2 ) {
1434
                $('#' + serverID + ' div.cpu-graph img.stats').attr("src", data.stats.cpuTimeSeries);
1435
                $('#' + serverID + ' div.network-graph img.stats').attr("src", data.stats.netTimeSeries);
1436
            }
1437
        }
1438
    });
1439
    return false;
1440
}
1441

    
1442
// create network
1443
function create_network(networkName){
1444
    // ajax post start call
1445
    var payload = {
1446
        "network": { "name": networkName }
1447
    };
1448

    
1449
    $.ajax({
1450
        url: API_URL + '/networks',
1451
        type: "POST",
1452
        contentType: "application/json",
1453
        dataType: "json",
1454
        data: JSON.stringify(payload),
1455
        timeout: TIMEOUT,
1456
        error: function(jqXHR, textStatus, errorThrown) {
1457
            try {
1458
                // close wizard and show error box
1459
                $("a#networkscreate").overlay().close();
1460
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1461
            } catch (err) {
1462
                ajax_error(-5, "UI Error", "Create network", err);
1463
            }
1464
        },
1465
        success: function(data, textStatus, jqXHR) {
1466
            if ( jqXHR.status == '202') {
1467
                try {
1468
                    console.info('created network ' + networkName);
1469
                } catch(err) {}
1470
                /*
1471
                On success of this call nothing happens.
1472
                When the UI gets the first update containing the created server,
1473
                the creation wizard is closed and the new network is inserted
1474
                to the DOM. This is done in update_networks_view()
1475
                */
1476
            } else {
1477
                // close wizard and show error box
1478
                $("a#networkscreate").overlay().close();
1479
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1480
            }
1481
        }
1482
    });
1483
    return false;
1484
}
1485

    
1486
// rename network
1487
function rename_network(networkID, networkName){
1488
    if (!networkID.length){
1489
        //ajax_success('DEFAULT');
1490
        return false;
1491
    }
1492
    // prepare payload
1493
    var payload = {
1494
        "network": {"name": networkName}
1495
    };
1496
    // ajax call
1497
    $.ajax({
1498
        url: API_URL + '/networks/' + networkID,
1499
        type: "PUT",
1500
        contentType: "application/json",
1501
        dataType: "json",
1502
        data: JSON.stringify(payload),
1503
        timeout: TIMEOUT,
1504
        error: function(jqXHR, textStatus, errorThrown) {
1505
            try {
1506
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1507
            } catch (err) {
1508
                ajax_error(-1, "UI Error", 'Rename network', err);
1509
            }
1510
        },
1511
        success: function(data, textStatus, jqXHR) {
1512
            if ( jqXHR.status == '204') {
1513
                try {
1514
                    console.info('renamed network' + networkID);
1515
                } catch(err) {}
1516
            } else {
1517
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1518
            }
1519
        }
1520
    });
1521
    return false;
1522
}
1523

    
1524
function delete_network(networkIDs){
1525
    if (!networkIDs.length){
1526
        //ajax_success('DEFAULT');
1527
        return false;
1528
    }
1529
    // get a network
1530
    var networkID = networkIDs.pop();
1531
    // ajax post destroy call can have an empty request body
1532
    var payload = {};
1533
    // ajax call
1534
    $.ajax({
1535
        url: API_URL + '/networks/' + networkID,
1536
        type: "DELETE",
1537
        contentType: "application/json",
1538
        dataType: "json",
1539
        data: JSON.stringify(payload),
1540
        timeout: TIMEOUT,
1541
        error: function(jqXHR, textStatus, errorThrown) {
1542
            try {
1543
                display_net_failure(jqXHR.status, networkID, 'Delete', jqXHR.responseText);
1544
            } catch (err) {
1545
                display_net_failure(0, networkID, 'Delete', jqXHR.responseText);
1546
            }
1547
        },
1548
        success: function(data, textStatus, jqXHR) {
1549
            if ( jqXHR.status == '204') {
1550
                try {
1551
                    console.info('deleted network ' + networkID);
1552
                } catch(err) {}
1553
                // continue with the rest of the servers
1554
                delete_network(networkIDs);
1555
            } else {
1556
                try {
1557
                    display_net_failure(jqXHR.status, networkID, 'Delete', jqXHR.responseText);
1558
                } catch (err) {
1559
                    display_net_failure(0, networkID, 'Delete', jqXHR.responseText);
1560
                }
1561
            }
1562
        }
1563
    });
1564
    return false;
1565
}
1566

    
1567
function add_server_to_network(networkID, serverIDs, serverNames, serverStates) {
1568
    if (!serverIDs.length){
1569
        // close the overlay when all the calls are made
1570
        $("a#add-machines-overlay").overlay().close();
1571
        return false;
1572
    }
1573
    // get a server
1574
    var serverID = serverIDs.pop();
1575
    var serverName = serverNames.pop();
1576
    var serverState = serverStates.pop();
1577
    // prepare payload
1578
    var payload = {
1579
            "add": { "serverRef": serverID }
1580
        };
1581
    // prepare ajax call
1582
    $.ajax({
1583
        url: API_URL + '/networks/' + networkID + '/action',
1584
        type: "POST",
1585
        contentType: "application/json",
1586
        dataType: "json",
1587
        data: JSON.stringify(payload),
1588
        timeout: TIMEOUT,
1589
        error: function(jqXHR, textStatus, errorThrown) {
1590
            try {
1591
                // close wizard and show error box
1592
                $("a#add-machines-overlay").data('overlay').close();
1593
                ajax_error(jqXHR.status, undefined, 'Add server to network', jqXHR.responseText);
1594
            } catch (err) {
1595
                ajax_error(-5, "UI Error", 'Add server to network', err);
1596
            }
1597
        },
1598
        success: function(data, textStatus, jqXHR) {
1599
            if ( jqXHR.status == '202') {
1600
                try {
1601
                    console.info('added server ' + serverID + ' to network ' + networkID);
1602
                } catch(err) {}
1603
                // toggle the reboot dialog
1604
                display_reboot_dialog(networkID, serverID, serverName, serverState);
1605
                // continue with the rest of the servers
1606
                add_server_to_network(networkID, serverIDs, serverNames, serverStates);
1607
            } else {
1608
                // close wizard and show error box
1609
                $("a#add-machines-overlay").data('overlay').close();
1610
                ajax_error(jqXHR.status, undefined, 'Add server to network', jqXHR.responseText);
1611
            }
1612
        }
1613
    });
1614
    return false;
1615
}
1616

    
1617
function remove_server_from_network(networkIDs, serverIDs, serverNames, serverStates) {
1618
    if (!networkIDs.length){
1619
        //ajax_success('DEFAULT');
1620
        return false;
1621
    }
1622
    // get a network and a server
1623
    var networkID = networkIDs.pop();
1624
    var serverID = serverIDs.pop();
1625
    var serverName = serverNames.pop();
1626
    var serverState = serverStates.pop();
1627
    // prepare payload
1628
    var payload = {
1629
            "remove": { "serverRef": serverID }
1630
        };
1631
    // prepare ajax call
1632
    $.ajax({
1633
        url: API_URL + '/networks/' + networkID + '/action',
1634
        type: "POST",
1635
        contentType: "application/json",
1636
        dataType: "json",
1637
        data: JSON.stringify(payload),
1638
        timeout: TIMEOUT,
1639
        error: function(jqXHR, textStatus, errorThrown) {
1640
            try {
1641
                ajax_error(jqXHR.status, undefined, 'Remove server form network', jqXHR.responseText);
1642
            } catch (err) {
1643
                ajax_error(-5, "UI Error", 'Remove server form network', err);
1644
            }
1645
        },
1646
        success: function(data, textStatus, jqXHR) {
1647
            if ( jqXHR.status == '202') {
1648
                try {
1649
                    console.info('deleted server ' + serverID + ' from network ' + networkID);
1650
                } catch(err) {}
1651
                // toggle the reboot dialog
1652
                display_reboot_dialog(networkID, serverID, serverName, serverState);
1653
                // continue with the rest of the servers
1654
                remove_server_form_network(networkIDs, serverIDs, serverNames, serverStates);
1655
            } else {
1656
                ajax_error(jqXHR.status, undefined, 'Remove server form network', jqXHR.responseText);
1657
            }
1658
        }
1659
    });
1660
    return false;
1661
}
1662

    
1663
function set_firewall(networkID, serverID, profile) {
1664
    if (!networkID.length || !serverID.length || !profile.length){
1665
        return false;
1666
    }
1667
    // prepare payload
1668
    var payload = {
1669
            "firewallProfile": { "profile": profile }
1670
        };
1671
    // prepare ajax call
1672
    $.ajax({
1673
        url: API_URL + '/servers/' + serverID + '/action',
1674
        type: "POST",
1675
        contentType: "application/json",
1676
        dataType: "json",
1677
        data: JSON.stringify(payload),
1678
        timeout: TIMEOUT,
1679
        error: function(jqXHR, textStatus, errorThrown) {
1680
            try {
1681
                ajax_error(jqXHR.status, undefined, 'Set firewall profile', jqXHR.responseText);
1682
            } catch (err) {
1683
                ajax_error(-5, "UI Error", 'Set firewall profile', err);
1684
            }
1685
        },
1686
        success: function(data, textStatus, jqXHR) {
1687
            if ( jqXHR.status == '202') {
1688
                try {
1689
                    console.info('for server ' + serverID + ' set firewall profile to ' + profile);
1690
                } catch(err) {}
1691
                //remove progress gif and toggle the content
1692
                $('div#net-' + networkID + '-server-' + serverID + ' button.firewall-apply').html(VARIOUS["APPLY"]);
1693
                $('div#net-' + networkID + '-server-' + serverID + ' button.firewall-apply').attr("disabled", false);
1694
                $('div#net-' + networkID + '-server-' + serverID + ' div.firewall-header').click();
1695
                // toggle the reboot dialog
1696
                var serverName = $('div#net-' + networkID + '-server-' + serverID + ' div.machine-name-div span.name').text();
1697
                var serverState = $('div#net-' + networkID + '-server-' + serverID + ' img.logo').attr('src').split('-')[1];
1698
                serverState = serverState.split('.')[0];
1699
                display_reboot_dialog(networkID, serverID, serverName, serverState);
1700
            } else {
1701
                ajax_error(jqXHR.status, undefined, 'Set firewall profile', jqXHR.responseText);
1702
            }
1703
        }
1704
    });
1705
    return false;
1706
}
1707

    
1708
// show the welcome screen
1709
function showWelcome() {
1710
    $("#view-select").fadeOut("fast");
1711
    $("#emptymachineslist").fadeIn("fast");
1712
    $("#machinesview").hide();
1713
}
1714

    
1715
// hide the welcome screen
1716
function hideWelcome() {
1717
    $("#emptymachineslist").fadeOut("fast");
1718
    $("#view-select").fadeIn("fast");
1719
    $("div#view-select").show();
1720
    $("#machinesview").show();
1721
}
1722

    
1723
function log_server_status_change(server_entry, new_status) {
1724
    // firebug console logging
1725
    try {
1726
        if ($("#machinesview-single").length > 0) {
1727
            console.info(server_entry.find("div.machine-details div.name").text() +
1728
                        ' from ' + server_entry.find(".state-label").text() +
1729
                        ' to ' + STATUSES[new_status]);
1730
        } else {
1731
            console.info(server_entry.find("div.name span.name").text() +
1732
                        ' from ' + server_entry.find(".status").text() +
1733
                        ' to ' + STATUSES[new_status]);
1734
        }
1735
    } catch(err) {}
1736
}
1737

    
1738
function get_flavor_params(flavorRef) {
1739
    var cpus, ram, disk;
1740
    if ( flavors.length > 0 ) {
1741
        var current_flavor = '';
1742
        for (i=0; i<flavors.length; i++) {
1743
            if (flavors[i]['id'] == flavorRef) {
1744
                current_flavor = flavors[i];
1745
            }
1746
        }
1747
        cpus = current_flavor['cpu'];
1748
        ram = current_flavor['ram'];
1749
        disk = current_flavor['disk'];
1750
    } else {
1751
        cpus = 'undefined';
1752
        ram = 'undefined';
1753
        disk = 'undefined';
1754
    }
1755
    return {'cpus': cpus, 'ram': ram, 'disk': disk};
1756
}
1757

    
1758
function get_image_params(imageRef) {
1759
    var image_name, image_size;
1760
    if ( images.length > 0 ) {
1761
        var current_image = '';
1762
        for (i=0; i<images.length; i++) {
1763
            if (images[i]['id'] == imageRef) {
1764
                current_image = images[i];
1765
            }
1766
        }
1767
        try {
1768
            image_name = current_image['name'];
1769
        } catch(err) { image_name = 'undefined'; }
1770
        try{
1771
            image_size = current_image['metadata']['values']['size'];
1772
        } catch(err) { image_size = 'undefined'; }
1773
    } else {
1774
        image_name = 'undefined';
1775
        image_size = 'undefined';
1776
    }
1777
    return {'name': image_name,'size': image_size};
1778
}
1779

    
1780
function get_public_ips(server) {
1781
    var ip4, ip6;
1782
    try {
1783
        if (server.addresses.values) {
1784
            $.each (server.addresses.values, function(i, value) {
1785
                if (value.id == 'public') {
1786
                    try {
1787
                        $.each (value.values, function(i, ip) {
1788
                            if (ip.version == '4') {
1789
                                ip4 = ip.addr;
1790
                            } else if (ip.version == '6') {
1791
                                ip6 = ip.addr;
1792
                            } else {
1793
                                ip4 = 'pending';
1794
                                ip6 = 'pending';
1795
                            }
1796
                        });
1797
                    } catch (err){
1798
                        try{console.info('Server ' + server.id + ' has invalid ips')}catch(err){};
1799
                        ip4 = 'pending';
1800
                        ip6 = 'pending';
1801
                    }
1802
                }
1803
            });
1804
        }
1805
    } catch (err) {
1806
        try{console.info('Server ' + server.id + ' has no network addresses')}catch(err){};
1807
        ip4 = 'pending';
1808
        ip6 = 'pending';
1809
    }
1810
    return {'ip4': ip4, 'ip6': ip6};
1811
}
1812

    
1813
function get_private_ips(server) {
1814

    
1815
}
1816

    
1817
function close_all_overlays() {
1818
        try {
1819
                $("a#networkscreate").overlay().close();
1820
        } catch(err) {}
1821
        try {
1822
                $('a#create').overlay().close();
1823
        } catch(err) {}
1824
        try {
1825
                $("a#add-machines-overlay").overlay().close();
1826
        } catch(err) {}
1827
        try {
1828
                $("a#metadata-scrollable").overlay().close();
1829
        } catch(err) {}
1830
        try {
1831
                $("a#msgbox").overlay().close();
1832
        } catch(err) {}
1833
        try {
1834
                $("a#feedbackbox").overlay().close();
1835
        } catch(err) {}
1836
}
1837

    
1838
// logout
1839
function user_session_logout() {
1840
    $.cookie("X-Auth-Token", null);
1841
    if (window.LOGOUT_REDIRECT !== undefined)
1842
    {
1843
        window.location = window.LOGOUT_REDIRECT;
1844
    } else {
1845
        window.location.reload();
1846
    }
1847
}
1848

    
1849
// action indicators
1850
function init_action_indicator_handlers(machines_view)
1851
{
1852
    // init once for each view
1853
    if (window.ACTION_ICON_HANDLERS == undefined)
1854
    {
1855
        window.ACTION_ICON_HANDLERS = {};
1856
    }
1857

    
1858
    if (machines_view in window.ACTION_ICON_HANDLERS)
1859
    {
1860
        return;
1861
    }
1862
    window.ACTION_ICON_HANDLERS[machines_view] = 1;
1863

    
1864
    if (machines_view == "list")
1865
    {
1866
        // totally different logic for list view
1867
        init_action_indicator_list_handlers();
1868
        return;
1869
    }
1870

    
1871
    function update_action_icon_indicators(force)
1872
    {
1873
        function show(el, action) {
1874
            $(".action-indicator", $(el)).attr("class", "action-indicator " + action);
1875
            $(".action-indicator", $(el)).show();
1876
        }
1877

    
1878
        function hide(el) {
1879
            $(".action-indicator", $(el)).hide();
1880
        }
1881

    
1882
        function get_pending_actions(el) {
1883
            return $(".confirm_single:visible", $(el));
1884
        }
1885

    
1886
        function other_indicators(el) {
1887
           return $("img.wave:visible, img.spinner:visible", $(el))
1888
        }
1889

    
1890
        $("div.machine:visible, div.single-container").each(function(index, el){
1891
            var el = $(el);
1892
            var pending = get_pending_actions(el);
1893
            var other = other_indicators(el);
1894
            var action = undefined;
1895
            var force_action = force;
1896
            var visible = $(el).css("display") == "block";
1897

    
1898
            if (force_action !==undefined && force_action.el !== el[0]) {
1899
                // force action for other vm
1900
                // skipping force action
1901
                force_action = undefined;
1902
            }
1903

    
1904
            if (force_action !==undefined && force_action.el === el[0]) {
1905
                action = force_action.action;
1906
            }
1907

    
1908
            if (other.length >= 1) {
1909
                return;
1910
            }
1911

    
1912
            if (pending.length >= 1 && force_action === undefined) {
1913
                action = $(pending.parent()).attr("class").replace("action-container","");
1914
            }
1915

    
1916
            if (action in {'console':''}) {
1917
                return;
1918
            }
1919

    
1920
            if (action !== undefined) {
1921
                show(el, action);
1922
            } else {
1923
                try {
1924
                    if (el.attr('id') == pending_actions[0][1])
1925
                    {
1926
                        return;
1927
                    }
1928
                } catch (err) {
1929
                }
1930
                hide(el);
1931
            }
1932

    
1933
        });
1934
    }
1935

    
1936
    // action indicators
1937
    $(".action-container").live('mouseover', function(evn) {
1938
        force_action = {'el': $(evn.currentTarget).parent().parent()[0], 'action':$(evn.currentTarget).attr("class").replace("action-container","")};
1939
        // single view case
1940
        if ($(force_action.el).attr("class") == "upper")
1941
        {
1942
            force_action.el = $(evn.currentTarget).parent().parent().parent()[0]
1943
        };
1944
        update_action_icon_indicators(force_action);
1945
    });
1946

    
1947
    $("img.spinner, img.wave").live('hide', function(){
1948
        update_action_icon_indicators();
1949
    });
1950
    // register events where icons should get updated
1951

    
1952
    // hide action indicator image on mouse out, spinner appear, wave appear
1953
    $(".action-container").live("mouseout", function(evn){
1954
        update_action_icon_indicators();
1955
    });
1956

    
1957
    $(".confirm_single").live("click", function(evn){
1958
        update_action_icon_indicators();
1959
    });
1960

    
1961
    $("img.spinner, img.wave").live('show', function(){
1962
        $("div.action-indicator").hide();
1963
    });
1964

    
1965
    $(".confirm_single button.no").live('click', function(evn){
1966
        $("div.action-indicator", $(evn.currentTarget).parent().parent()).hide();
1967
    });
1968

    
1969
    $(".confirm_multiple button.no").click(function(){
1970
        $("div.action-indicator").hide();
1971
    });
1972

    
1973
    $(".confirm_multiple button.yes").click(function(){
1974
        $("div.action-indicator").hide();
1975
    });
1976
}
1977

    
1978
function init_action_indicator_list_handlers()
1979
{
1980
    var skip_actions = { 'connect':'','details':'' };
1981

    
1982
    var has_pending_confirmation = function()
1983
    {
1984
        return $(".confirm_multiple:visible").length >= 1
1985
    }
1986

    
1987
    function update_action_indicator_icons(force_action, skip_pending)
1988
    {
1989
        // pending action based on the element class
1990
        var pending_action = $(".selected", $(".actions"))[0];
1991
        var selected = get_list_view_selected_machine_rows();
1992

    
1993
        // reset previous state
1994
        list_view_hide_action_indicators();
1995

    
1996
        if (pending_action == undefined && !force_action)
1997
        {
1998
            // no action selected
1999
            return;
2000
        }
2001

    
2002
        if (force_action != undefined)
2003
        {
2004
            // user forced action choice
2005
            var action_class = force_action;
2006
        } else {
2007
            // retrieve action name (reboot, stop, etc..)
2008
            var action_class = $(pending_action).attr("id").replace("action-","");
2009
        }
2010

    
2011
        selected.each(function(index, el) {
2012
            if (has_pending_confirmation() && skip_pending)
2013
            {
2014
                return;
2015
            }
2016
            var el = $(el);
2017
            var logo = $("img.list-logo", el);
2018
            $(".action-indicator", el).remove();
2019
            var cls = "action-indicator " + action_class;
2020
            // add icon div
2021
            logo.after('<div class="' + cls + '"></div>');
2022
            // hide os logo
2023
            $("img.list-logo", el).hide();
2024
        });
2025
    }
2026

    
2027
    // on mouseover we force the images to the hovered action
2028
    $(".actions a").live("mouseover", function(evn) {
2029
        var el = $(evn.currentTarget);
2030
        if (!el.hasClass("enabled"))
2031
        {
2032
            return;
2033
        }
2034
        var action_class = el.attr("id").replace("action-","");
2035
        if (action_class in skip_actions)
2036
        {
2037
            return;
2038
        }
2039
        update_action_indicator_icons(action_class, false);
2040
    });
2041

    
2042

    
2043
    // register events where icons should get updated
2044
    $(".actions a.enabled").live("click", function(evn) {
2045
        // clear previous selections
2046
        $("a.selected").removeClass("selected");
2047

    
2048
        var el = $(evn.currentTarget);
2049
        el.addClass("selected");
2050
        update_action_indicator_icons(undefined, false);
2051
    });
2052

    
2053
    $(".actions a").live("mouseout", function(evn) {
2054
        update_action_indicator_icons(undefined, false);
2055
    });
2056

    
2057
    $(".confirm_multiple button.no").click(function(){
2058
        list_view_hide_action_indicators();
2059
    });
2060

    
2061
    $(".confirm_multiple button.yes").click(function(){
2062
        list_view_hide_action_indicators();
2063
    });
2064

    
2065
    $("input[type=checkbox]").live('change', function(){
2066
        // pending_actions will become empty on every checkbox click/change
2067
        // line 154 machines_list.html
2068
        pending_actions = [];
2069
        if (pending_actions.length == 0)
2070
        {
2071
            $(".confirm_multiple").hide();
2072
            $("a.selected").each(function(index, el){$(el).removeClass("selected")});
2073
        }
2074
        update_action_indicator_icons(undefined, false);
2075
    });
2076

    
2077
}
2078

    
2079
function list_view_hide_action_indicators()
2080
{
2081
    $("tr td .action-indicator").remove();
2082
    $("tr td img.list-logo").show();
2083
}
2084

    
2085
function get_list_view_selected_machine_rows()
2086
{
2087
    var table = $("table.list-machines");
2088
    var rows = $("tr:has(input[type=checkbox]:checked)",table);
2089
    return rows;
2090
}
2091

    
2092
// machines images utils
2093
function set_machine_os_image(machine, machines_view, state, os, skip_reset_states, remove_state) {
2094
    var views_map = {'single': '.single-image', 'icon': '.logo'};
2095
    var states_map = {'on': 'state1', 'off': 'state3', 'hover': 'state4', 'click': 'state2'}
2096
    var sizes_map = {'single': 'large', 'icon': 'medium'}
2097

    
2098
    var size = sizes_map[machines_view];
2099
    var img_selector = views_map[machines_view];
2100
    var cls = states_map[state];
2101

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

    
2105
    var el = $(img_selector, machine);
2106
    var current_img = el.css("backgroundImage");
2107
    if (os == undefined){
2108
        new_img = current_img;
2109
    }
2110

    
2111
    // os changed
2112
    el.css("backgroundImage", new_img);
2113

    
2114
    // reset current state
2115
    if (skip_reset_states === undefined)
2116
    {
2117
        el.removeClass("single-image-state1");
2118
        el.removeClass("single-image-state2");
2119
        el.removeClass("single-image-state3");
2120
        el.removeClass("single-image-state4");
2121
    }
2122

    
2123
    if (remove_state !== undefined)
2124
    {
2125
        remove_state = "single-image-" + states_map[remove_state];
2126
        el.removeClass(remove_state);
2127
        return;
2128
    }
2129
    
2130
    // set proper state
2131
    el.addClass("single-image-" + cls);
2132
}
2133

    
2134

    
2135
// generic info box
2136
function show_feedback_form() {
2137
    var box = $("#feedback-form");
2138
    box.addClass("notification-box");
2139

    
2140
    // initialize
2141
    box.find(".form-container").show();
2142
    box.find("textarea").val("");
2143
    box.find(".message").hide();
2144

    
2145
    var triggers = $("a#feedbackbox").overlay({
2146
        // some mask tweaks suitable for modal dialogs
2147
        mask: '#666',
2148
        top: '10px',
2149
        fixed: false,
2150
        closeOnClick: false,
2151
        oneInstance: false,
2152
        load: false,
2153
        onClose: function () {
2154
            // With partial refresh working properly,
2155
            // it is no longer necessary to refresh the whole page
2156
            // choose_view();
2157
        }
2158
    });
2159
    
2160
    $("#feedback-form form").unbind("submit");
2161
    $("#feedback-form form").submit(function(event) {
2162
        event.preventDefault();
2163
            
2164
        // empty msg
2165
        if ($("textarea.feedback-text").val().replace(/^\s*|\s*$/,"") == "") {
2166
            alert($(".empty-error-msg", this).text());
2167
            return;
2168
        }
2169

    
2170
        $("textarea.data-text", this).val("").val(JSON.stringify(get_user_data()));
2171

    
2172
        $.ajax({
2173
            url: FEEDBACK_URL,
2174
            data: $(this).serialize(),
2175
            type: "POST",
2176
            // show loading
2177
            beforeSend: function() {box.find(".form-container").hide(); box.find(".sending").fadeIn() },
2178
            // hide form
2179
            complete: function() { box.find(".form-container").hide(); box.find(".sending").hide() },
2180
            // on success display success message
2181
            success: function() { box.find(".success").fadeIn(); box.find(".sending").hide() },
2182
            // display error message
2183
            error: function() { box.find(".errormsg").fadeIn(); box.find(".sending").hide() }
2184
        })
2185
    });
2186
    
2187
    $("a#feedbackbox").data('overlay').load();
2188
    return false;
2189
}
2190

    
2191
function get_user_data(extra_data) {
2192
    return $.extend({
2193
        'cookie': $.cookie("X-Auth-Token"),
2194
        'servers': $.extend({}, servers),
2195
        'client': {'browser': $.browser, 'screen': $.extend({}, screen), 'client': $.client},
2196
        'dates': {'now': new Date, 'lastUpdate': changes_since_date}
2197
    }, extra_data);
2198
}
2199

    
2200
function msg_box(config) {
2201
    var config = $.extend({'title':'Info message', 'content': 'this is an info message', 'ajax': false, 'extra':false}, config);
2202
    // prepare the error message
2203
    // bring up success notification
2204

    
2205
    var box = $("#notification-box");
2206
    box.addClass("notification-box");
2207
    box.addClass('success');
2208
    box.removeClass('error');
2209

    
2210
    var sel = function(s){return $(s, box)};
2211
    // reset texts
2212
    sel("h3 span.header-box").html("");
2213
    sel(".sub-text").html("");
2214
    sel(".password-container .password").html("");
2215
    sel("div.machine-now-building").html("");
2216
    
2217

    
2218
    // apply msg box contents
2219
    sel("h3 span.header-box").html(config.title);
2220
    sel("div.machine-now-building").html(config.content);
2221
    sel(".popup-header").removeClass("popup-header-error");
2222
    box.removeClass("popup-border-error");
2223
    sel(".popup-details").removeClass("popup-details-error");
2224
    sel(".popup-separator").removeClass("popup-separator-error");
2225
    
2226
    sel(".password-container").hide();
2227
    if (config.extra) {
2228
        sel(".password-container .password").html(config.extra);
2229
        sel(".password-container").show();
2230
    }
2231

    
2232
    var triggers = $("a#msgbox").overlay({
2233
        // some mask tweaks suitable for modal dialogs
2234
        mask: '#666',
2235
        top: '10px',
2236
        closeOnClick: false,
2237
        oneInstance: false,
2238
        load: false,
2239
        fixed: false,
2240
        onClose: function () {
2241
            // With partial refresh working properly,
2242
            // it is no longer necessary to refresh the whole page
2243
            // choose_view();
2244
        }
2245
    });
2246
    $("a#msgbox").data('overlay').load();
2247
    
2248
    var parse_data = config.parse_data || false;
2249
    var load_html = config.html || false;
2250
    var user_success = config.success || false;
2251
    config.ajax = config.ajax || {};
2252

    
2253
    // requested to show remote data in msg_box
2254
    if (config.ajax) {
2255
        $.ajax($.extend({ 
2256
            url:config.ajax, 
2257
            success: function(data){
2258
                // we want to get our data parsed before
2259
                // placing them in content
2260
                if (parse_data) {
2261
                    data = parse_data(data);
2262
                }
2263

    
2264
                // no json response
2265
                // load html body
2266
                if (load_html) {
2267
                    sel("div.machine-now-building").html(data);
2268
                } else {
2269

    
2270
                    if (data.title) {
2271
                        sel("h3 span.header-box").text(data.title);
2272
                    }
2273

    
2274
                    if (data.content) {
2275
                        sel("div.machine-now-building").html(data.content);
2276
                    }
2277
                    if (data.extra) {
2278
                        sel(".password-container .password").html(data.extra);
2279
                        sel(".password-container").show();
2280
                    }
2281
                    if (data.subinfo) {
2282
                        sel(".sub-text").html(data.subinfo);
2283
                    } else {
2284
                        sel(".sub-text").html("");
2285
                    }
2286
                }
2287

    
2288
                if (user_success) {
2289
                    user_success($("div.machine-now-building"));
2290
                }
2291
            },
2292
            error: function(xhr, status, err) {
2293
                ajax_error(-5, "UI Error", "Machine connect", err);
2294
            }
2295
        }, config.ajax_config));
2296
    }
2297
    return false;
2298
}
2299

    
2300

    
2301
function show_invitations() {
2302

    
2303
    handle_invitations = function(el) {
2304
        el.addClass("invitations");
2305
        var cont = el;
2306
        var form = $(el).find("form");
2307

    
2308
        // remove garbage rows that stay in DOM between requests
2309
        $(".removable-field-row:hidden").remove();
2310
        $("#invform #removable-name-container-1").dynamicField();
2311
        
2312
        $(".invitations-left").hide();
2313
        $("#notification-box .header-box").html("");
2314
        $("#notification-box .header-box").html(window.INVITATIONS_TITLE + " " + $($(".invitations-left")[0]).text());
2315

    
2316
        form.submit(function(evn){
2317
            evn.preventDefault();
2318
            $.post(form.attr("action"), form.serialize(), function(data) {
2319
                $(cont).html(data); 
2320
                handle_invitations(cont);
2321
            });
2322
            return false;
2323
        });
2324
    }
2325

    
2326
    msg_box({title:window.INVITATIONS_TITLE, content:'', ajax:INVITATIONS_URL, html:true, success: function(el){ 
2327
        handle_invitations(el)}
2328
    });
2329
}
2330