Statistics
| Branch: | Tag: | Revision:

root / ui / static / synnefo.js @ d790d9b7

History | View | Annotate | Download (78.7 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
// http://stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area 
94
$.fn.setCursorPosition = function(pos) {
95
    if ($(this).get(0).setSelectionRange) {
96
      $(this).get(0).setSelectionRange(pos, pos);
97
    } else if ($(this).get(0).createTextRange) {
98
      var range = $(this).get(0).createTextRange();
99
      range.collapse(true);
100
      range.moveEnd('character', pos);
101
      range.moveStart('character', pos);
102
      range.select();
103
    }
104
}
105

    
106
// jquery show/hide events
107
var _oldshow = $.fn.show;
108
$.fn.show = function(speed, callback) {
109
    $(this).trigger('show');
110
    return _oldshow.apply(this,arguments);
111
}
112
var _oldhide = $.fn.hide;
113
$.fn.hide = function(speed, callback) {
114
    $(this).trigger('hide');
115
    return _oldhide.apply(this,arguments);
116
}
117

    
118
function ISODateString(d){
119
    //return a date in an ISO 8601 format using UTC.
120
    //do not include time zone info (Z) at the end
121
    //taken from the Mozilla Developer Center
122
    function pad(n){ return n<10 ? '0'+n : n }
123
    return  d.getUTCFullYear()+ '-' +
124
            pad(d.getUTCMonth()+1) + '-' +
125
            pad(d.getUTCDate()) + 'T' +
126
            pad(d.getUTCHours()) + ':' +
127
            pad(d.getUTCMinutes()) + ':' +
128
            pad(d.getUTCSeconds()) +'Z'
129
}
130

    
131
function parse_error(responseText, errorCode){
132
    var errors = [];
133
    try {
134
        responseObj = JSON.parse(responseText);
135
    }
136
    catch(err) {
137
        errors[0] = {'code': errorCode};
138
        return errors;
139
    }
140
    for (var err in responseObj){
141
        errors[errors.length] = responseObj[err];
142
    }
143
    return errors;
144
}
145

    
146
// indexOf prototype for IE
147
if (!Array.prototype.indexOf) {
148
  Array.prototype.indexOf = function(elt /*, from*/) {
149
    var len = this.length;
150
    var from = Number(arguments[1]) || 0;
151
    from = (from < 0)
152
         ? Math.ceil(from)
153
         : Math.floor(from);
154
    if (from < 0)
155
      from += len;
156

    
157
    for (; from < len; from++) {
158
      if (from in this &&
159
          this[from] === elt)
160
        return from;
161
    }
162
    return -1;
163
  };
164
}
165

    
166
// trim prototype for IE
167
if(typeof String.prototype.trim !== 'function') {
168
    String.prototype.trim = function() {
169
        return this.replace(/^\s+|\s+$/g, '');
170
    }
171
}
172

    
173
function update_confirmations() {
174
    // hide all confirm boxes to begin with
175
    $('#machines-pane div.confirm_single').hide();
176
    $('#machines-pane div.confirm_multiple').hide();
177
    var action_type = [];
178
    // standard view or single view
179
    if ($.cookie("view") == '0' || $.cookie("view") == '2') {
180
        for (var i=0; i<pending_actions.length; i++) {
181
            // show single confirms
182
            if (pending_actions[i][0] == reboot) {
183
                action_type = "reboot";
184
            } else if (pending_actions[i][0] == shutdown) {
185
                action_type = "shutdown";
186
            } else if (pending_actions[i][0] == start) {
187
                action_type = "start";
188
            } else if (pending_actions[i][0] == open_console) {
189
                action_type = "console";
190
            } else {
191
                action_type = "destroy";
192
            }
193
            $("#machines-pane #" + pending_actions[i][1] +
194
            " div.action-container." + action_type + " div.confirm_single").show();
195
        }
196
    }
197
    // if more than one pending action show multiple confirm box
198
    if (pending_actions.length>1 || $.cookie("view") == '1' && pending_actions.length == 1){
199
        $('#machines-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
200
        $('#machines-pane div.confirm_multiple').show();
201
    }
202
}
203

    
204
function update_network_confirmations(){
205
    // hide all confirm boxes to begin with
206
    $('#networks-pane div.confirm_multiple').hide();
207

    
208
    for (var i=0;i<pending_actions.length;i++){
209
        // show single confirms depending on the action
210
        if (pending_actions[i][0] == delete_network) {
211
            $("#networks-pane div.network#net-"+pending_actions[i][1]).children('.confirm_single').show();
212
        } else if (pending_actions[i][0] == remove_server_from_network) {
213
            $("#networks-pane div.network #net-"+pending_actions[i][1]+"-server-"+pending_actions[i][2]).children('.confirm_single').show();
214
        } // else {}
215
    }
216

    
217
    // if more than one pending action show multiple confirm box
218
    if (pending_actions.length > 1){
219
        $('#networks-pane div.confirm_multiple span.actionLen').text(pending_actions.length);
220
        $('#networks-pane div.confirm_multiple').show();
221
    }
222
}
223

    
224
function list_view() {
225
    changes_since = 0; // to reload full list
226
    pending_actions = []; // clear pending actions
227
    update_confirmations();
228
    clearTimeout(deferred);    // clear old deferred calls
229
    try {
230
        update_request.abort(); // cancel pending ajax updates
231
        load_request.abort();
232
    }catch(err){}
233
    $.cookie("view", '1'); // set list cookie
234
    uri = $("a#list").attr("href");
235
    load_request = $.ajax({
236
        url: uri,
237
        type: "GET",
238
        timeout: TIMEOUT,
239
        dataType: "html",
240
        error: function(jqXHR, textStatus, errorThrown) {
241
            return false;
242
        },
243
        success: function(data, textStatus, jqXHR) {
244
            $("a#list")[0].className += ' activelink';
245
            $("a#standard")[0].className = '';
246
            $("a#single")[0].className = '';
247
            $("div#machinesview").html(data);
248
        }
249
    });
250
    return false;
251
}
252

    
253
function single_view() {
254
    changes_since = 0; // to reload full list
255
    pending_actions = []; // clear pending actions
256
    update_confirmations();
257
    clearTimeout(deferred);    // clear old deferred calls
258
    try {
259
        update_request.abort(); // cancel pending ajax updates
260
        load_request.abort();
261
    }catch(err){}
262
    $.cookie("view", '2'); // set list cookie
263
    uri = $("a#single").attr("href");
264
    load_request = $.ajax({
265
        url: uri,
266
        type: "GET",
267
        timeout: TIMEOUT,
268
        dataType: "html",
269
        error: function(jqXHR, textStatus, errorThrown) {
270
            return false;
271
        },
272
        success: function(data, textStatus, jqXHR) {
273
            $("a#single")[0].className += ' activelink';
274
            $("a#standard")[0].className = '';
275
            $("a#list")[0].className = '';
276
            $("div#machinesview").html(data);
277
        }
278
    });
279
    return false;
280
}
281

    
282
function standard_view() {
283
    changes_since = 0; // to reload full list
284
    pending_actions = []; // clear pending actions
285
    update_confirmations();
286
    clearTimeout(deferred);    // clear old deferred calls
287
    try {
288
        update_request.abort() // cancel pending ajax updates
289
        load_request.abort();
290
    }catch(err){}
291
    $.cookie("view", '0');
292
    uri = $("a#standard").attr("href");
293
    load_request = $.ajax({
294
        url: uri,
295
        type: "GET",
296
        timeout: TIMEOUT,
297
        dataType: "html",
298
        error: function(jqXHR, textStatus, errorThrown) {
299
            return false;
300
        },
301
        success: function(data, textStatus, jqXHR) {
302
            $("a#standard")[0].className += ' activelink';
303
            $("a#list")[0].className = '';
304
            $("a#single")[0].className = '';
305
            $("div#machinesview").html(data);
306
        }
307
    });
308
    return false;
309
}
310

    
311
function choose_view() {
312
    if ($.cookie("view")=='1') {
313
        list_view();
314
    } else if ($.cookie("view")=='2'){
315
        single_view();
316
    } else {
317
        standard_view();
318
    }
319
}
320

    
321
// return value from metadata key "OS", if it exists
322
function os_icon(metadata) {
323
    if (!metadata) {
324
        return 'okeanos';
325
    }
326
    if (metadata.values.OS == undefined || metadata.values.OS == '') {
327
        return 'okeanos';
328
    } else {
329
        if (os_icons.indexOf(metadata.values.OS) == -1) {
330
            return 'okeanos';
331
        } else {
332
            return metadata.values.OS;
333
        }
334
    }
335
}
336

    
337
function os_icon_from_value(metadata) {
338
    if (!metadata) {
339
        return 'okeanos';
340
    }
341
if (metadata == undefined || metadata == '') {
342
        return 'okeanos';
343
    } else {
344
        if (os_icons.indexOf(metadata) == -1) {
345
            return 'okeanos';
346
        } else {
347
            return metadata;
348
        }
349
    }
350
}
351

    
352
// get and show a list of running and terminated machines
353
function update_vms(interval) {
354
    try{ console.info('updating machines'); } catch(err){}
355
    var uri= API_URL + '/servers/detail';
356

    
357
    if (changes_since != 0)
358
        uri+='?changes-since='+changes_since
359

    
360
    update_request = $.ajax({
361
        cache: false,
362
        url: uri,
363
        type: "GET",
364
        timeout: TIMEOUT,
365
        dataType: "json",
366
        error: function(jqXHR, textStatus, errorThrown) {
367
            // don't forget to try again later
368
            if (interval) {
369
                clearTimeout(deferred);    // clear old deferred calls
370
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
371
            }
372
            // as for now, just show an error message
373
            try { console.info('update_vms errback:' + jqXHR.status ) } catch(err) {}
374
            try {
375
                ajax_error(jqXHR.status, undefined, 'Update VMs', jqXHR.responseText);
376
            } catch(err) {
377
                ajax_error(-5, "UI Error", 'Update VMs', err);
378
            }
379
            return false;
380
            },
381
        success: function(data, textStatus, jqXHR) {
382
            // create changes_since string if necessary
383
            if (jqXHR.getResponseHeader('Date') != null){
384
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
385
                changes_since = ISODateString(changes_since_date);
386
            }
387

    
388
            if (interval) {
389
                clearTimeout(deferred);    // clear old deferred calls
390
                deferred = setTimeout(function() {update_vms(interval);},interval,interval);
391
            }
392

    
393
            if (jqXHR.status == 200 || jqXHR.status == 203) {
394
                try {
395
                    //servers = data.servers.values;
396
                    update_servers_data(data.servers.values, data);
397
                    update_machines_view(data);
398
                } catch(err) { ajax_error(-5, "UI Error", 'Update VMs', err);}
399
            } else if (jqXHR.status != 304){
400
                try { console.info('update_vms callback:' + jqXHR.status ) } catch(err) {}
401
                /*
402
                FIXME:  Here it should return the error, however Opera does not support 304.
403
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
404
                        304, which should be corrected (Bug #317).
405
                */
406
                // ajax_error(jqXHR.status, "Ajax error", 'Update VMs', jqXHR.responseText);
407
            }
408
            return false;
409
        }
410
    });
411
    return false;
412
}
413

    
414
function update_servers_data(servers_update, data) {
415
    $(window).trigger("vm:update", servers_update, data);
416

    
417
    // first call
418
    if (!window.servers || window.servers.length == 0) {
419
        window.servers = servers_update;
420
        return;
421
    }
422
    
423
    // server exists helper
424
    server_exists = function(server) {
425
        var id = server.id;
426
        var found = false;
427
        var index = 0;
428
        $.each(servers, function(i, s) {
429
            if (s.id == id) { found = true, index = i };
430
        });
431
        if (found)
432
            return [found, index];
433

    
434
        return false;
435
    }
436

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

    
498
// get a list of running and terminated machines, used in network view
499
function update_networks(interval) {
500
    try{ console.info('updating networks'); } catch(err){}
501
    var uri= API_URL + '/servers/detail';
502

    
503
    if (changes_since != 0)
504
        uri+='?changes-since='+changes_since
505

    
506
    update_request = $.ajax({
507
        cache: false,
508
        url: uri,
509
        type: "GET",
510
        timeout: TIMEOUT,
511
        dataType: "json",
512
        error: function(jqXHR, textStatus, errorThrown) {
513
            // don't forget to try again later
514
            if (interval) {
515
                clearTimeout(deferred);    // clear old deferred calls
516
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
517
            }
518
            // as for now, just show an error message
519
            try { console.info('update_networks errback:' + jqXHR.status ) } catch(err) {}
520
            try {
521
                ajax_error(jqXHR.status, undefined, 'Update networks', jqXHR.responseText);
522
            } catch(err) {
523
                ajax_error(-5, "UI Error", 'Update networks', err);
524
            }
525
            return false;
526
            },
527
        success: function(data, textStatus, jqXHR) {
528
            // create changes_since string if necessary
529
            if (jqXHR.getResponseHeader('Date') != null){
530
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
531
                changes_since = ISODateString(changes_since_date);
532
            }
533

    
534
            if (interval) {
535
                clearTimeout(deferred);    // clear old deferred calls
536
                deferred = setTimeout(function() {update_networks(interval);},interval,interval);
537
            }
538

    
539
            if (jqXHR.status == 200 || jqXHR.status == 203) {
540
                try {
541
                    //servers = data.servers.values;
542
                    update_servers_data(data.servers.values, data);
543
                    jQuery.parseJSON(data);
544
                    update_network_names(data);
545
                } catch(err) { ajax_error(-5, "UI Error", 'Update networks', err);}
546
            } else if (jqXHR.status == 304) {
547
                update_network_names();
548
            }
549
            else {
550
                try { console.info('update_networks callback:' + jqXHR.status ) } catch(err) {}
551
                /*
552
                FIXME:  Here it should return the error, however Opera does not support 304.
553
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
554
                        304, which should be corrected (Bug #317).
555
                */
556
                //ajax_error(jqXHR.status, undefined, 'Update networks', jqXHR.responseText);
557
                update_network_names();
558
            }
559
            return false;
560
        }
561
    });
562
    return false;
563
}
564

    
565
// get and show a list of public and private networks
566
function update_network_names(servers_data) {
567
    try{ console.info('updating network names'); } catch(err){}
568
    var uri= API_URL + '/networks/detail';
569

    
570
    if (networks_changes_since != 0)
571
        //FIXME: Comment out the following, until metadata do not 304 when changed
572
        uri+='?changes-since=' + networks_changes_since
573

    
574
    update_request = $.ajax({
575
        cache: false,
576
        url: uri,
577
        type: "GET",
578
        timeout: TIMEOUT,
579
        dataType: "json",
580
        error: function(jqXHR, textStatus, errorThrown) {
581
            // as for now, just show an error message
582
            try {
583
                console.info('update_network names errback:' + jqXHR.status )
584
            } catch(err) {}
585
            try {
586
                ajax_error(jqXHR.status, undefined, 'Update network names', jqXHR.responseText);
587
            } catch(err) {
588
                ajax_error(-5, "UI Error", 'Update network names', err);
589
            }
590
            return false;
591
            },
592
        success: function(data, textStatus, jqXHR) {
593
            // create changes_since string if necessary
594
            if (jqXHR.getResponseHeader('Date') != null){
595
                changes_since_date = new Date(jqXHR.getResponseHeader('Date'));
596
                networks_changes_since = ISODateString(changes_since_date);
597
            }
598

    
599
            if (jqXHR.status == 200 || jqXHR.status == 203) {
600
                try {
601
                    networks = data.networks.values;
602
                    jQuery.parseJSON(data);
603
                    update_networks_view(servers_data, data);
604
                } catch(err) {
605
                    ajax_error(-5, "UI Error", 'Update network names', err);
606
                }
607
            } else if (jqXHR.status == 304) {
608
                update_networks_view(servers_data);
609
            } else if (jqXHR.status != 304){
610
                try { console.info('update_network_names callback:' + jqXHR.status ) } catch(err) {}
611
                /*
612
                FIXME:  Here it should return the error, however Opera does not support 304.
613
                        Instead 304 it returns 0. To dealt with this we treat 0 as an
614
                        304, which should be corrected (Bug #317).
615
                */
616
                //ajax_error(jqXHR.status, undefined, 'Update network names', jqXHR.responseText);
617
                update_networks_view(servers_data);
618
            }
619
            return false;
620
        }
621
    });
622
    return false;
623
}
624

    
625
// get and show a list of available standard and custom images
626
function update_images() {
627
    $.ajax({
628
        url: API_URL + '/images/detail',
629
        type: "GET",
630
        //async: false,
631
        dataType: "json",
632
        timeout: TIMEOUT,
633
        error: function(jqXHR, textStatus, errorThrown) {
634
                    try {
635
                        ajax_error(jqXHR.status, undefined, 'Update Images', jqXHR.responseText);
636
                    } catch(err) {
637
                        ajax_error(-5, "UI error", 'Update Images', err);
638
                    }
639
                },
640
        success: function(data, textStatus, jqXHR) {
641
            try {
642
                images = data.images.values;
643
                jQuery.parseJSON(data);
644
                update_wizard_images();
645
            } catch(err){
646
                ajax_error("NO_IMAGES");
647
            }
648
        }
649
    });
650
    return false;
651
}
652

    
653
function update_wizard_images() {
654
    if ($("ul#standard-images li").toArray().length + $("ul#custom-images li").toArray().length == 0) {
655
        $.each(images, function(i,image){
656
            var img = $('#image-template').clone().attr("id","img-"+image.id).fadeIn("slow");
657
            img.find("label").attr('for',"img-radio-" + image.id);
658
            img.find(".image-title").text(image.name);
659
            if (image.metadata) {
660
                if (image.metadata.values.description != undefined) {
661
                    img.find(".description").text(image.metadata.values.description);
662
                }
663
                if (image.metadata.values.size != undefined) {
664
                    img.find("#size").text(image.metadata.values.size);
665
                }
666
            }
667
            img.find("input.radio").attr('id',"img-radio-" + image.id);
668
            if (i==0) img.find("input.radio").attr("checked","checked");
669
            var image_logo = os_icon(image.metadata);
670
            img.find("img.image-logo").attr('src','static/icons/os/'+image_logo+'.png');
671
            if (image.metadata) {
672
                if (image.metadata.values.serverId != undefined) {
673
                    img.appendTo("ul#custom-images");
674
                } else {
675
                    img.appendTo("ul#standard-images");
676
                }
677
            } else {
678
                img.appendTo("ul#standard-images");
679
            }
680
        });
681
    }
682
}
683

    
684
function update_wizard_flavors(){
685
    // sliders for selecting VM flavor
686
    $("#cpu:range").rangeinput({min:0,
687
                               value:0,
688
                               step:1,
689
                               progress: true,
690
                               max:cpus.length-1});
691

    
692
    $("#storage:range").rangeinput({min:0,
693
                               value:0,
694
                               step:1,
695
                               progress: true,
696
                               max:disks.length-1});
697

    
698
    $("#ram:range").rangeinput({min:0,
699
                               value:0,
700
                               step:1,
701
                               progress: true,
702
                               max:ram.length-1});
703
    $("#small").click();
704

    
705
    // update the indicators when sliding
706
    $("#cpu:range").data().rangeinput.onSlide(function(event,value){
707
        $("#cpu-indicator")[0].value = cpus[Number(value)];
708
        $("#cpu-indicator").addClass('selectedrange');
709
    });
710
    $("#cpu:range").data().rangeinput.change(function(event,value){
711
        $("#cpu-indicator")[0].value = cpus[Number(value)];
712
        $("#custom").click();
713
        $("#cpu-indicator").removeClass('selectedrange');
714
    });
715
    $("#ram:range").data().rangeinput.onSlide(function(event,value){
716
        $("#ram-indicator")[0].value = ram[Number(value)];
717
        $("#ram-indicator").addClass('selectedrange');
718
    });
719
    $("#ram:range").data().rangeinput.change(function(event,value){
720
        $("#ram-indicator")[0].value = ram[Number(value)];
721
        $("#custom").click();
722
        $("#ram-indicator").removeClass('selectedrange');
723
    });
724
    $("#storage:range").data().rangeinput.onSlide(function(event,value){
725
        $("#storage-indicator")[0].value = disks[Number(value)];
726
        $("#storage-indicator").addClass('selectedrange');
727
    });
728
    $("#storage:range").data().rangeinput.change(function(event,value){
729
        $("#storage-indicator")[0].value = disks[Number(value)];
730
        $("#custom").click();
731
        $("#storage-indicator").removeClass('selectedrange');
732
    });
733
}
734

    
735
Array.prototype.unique = function () {
736
    var r = new Array();
737
    o:for(var i = 0, n = this.length; i < n; i++)
738
    {
739
        for(var x = 0, y = r.length; x < y; x++)
740
        {
741
            if(r[x]==this[i])
742
            {
743
                continue o;
744
            }
745
        }
746
        r[r.length] = this[i];
747
    }
748
    return r;
749
}
750

    
751
// get and configure flavor selection
752
function update_flavors() {
753
    $.ajax({
754
        url: API_URL + '/flavors/detail',
755
        type: "GET",
756
        //async: false,
757
        dataType: "json",
758
        timeout: TIMEOUT,
759
        error: function(jqXHR, textStatus, errorThrown) {
760
            try {
761
                ajax_error(jqXHR.status, undefined, 'Update Flavors', jqXHR.responseText);
762
            } catch (err) {
763
                ajax_error(-5, "UI Error", "Update Flavors", err);
764
            }
765
            // start updating vm list
766
            update_vms(UPDATE_INTERVAL);
767
        },
768
        success: function(data, textStatus, jqXHR) {
769

    
770
            try {
771
                flavors = data.flavors.values;
772
                jQuery.parseJSON(data);
773
                $.each(flavors, function(i, flavor) {
774
                    cpus[i] = flavor['cpu'];
775
                    disks[i] = flavor['disk'];
776
                    ram[i] = flavor['ram'];
777
                });
778
                cpus = cpus.unique();
779
                disks = disks.unique();
780
                ram = ram.unique();
781
                update_wizard_flavors();
782
            } catch(err){
783
                ajax_error("NO_FLAVORS");
784
            }
785
            // start updating vm list
786
            update_vms(UPDATE_INTERVAL);
787
        }
788
    });
789
    return false;
790
}
791

    
792
// return flavorRef from cpu, disk, ram values
793
function identify_flavor(cpu, disk, ram){
794
    for (i=0;i<flavors.length;i++){
795
        if (flavors[i]['cpu'] == cpu && flavors[i]['disk']==disk && flavors[i]['ram']==ram) {
796
            return flavors[i]['id']
797
        }
798
    }
799
    return 0;
800
}
801

    
802
// return image entry from imageRef
803
function get_image(imageRef) {
804
    for (i=0;i<images.length;i++){
805
        if (images[i]['id'] == imageRef) {
806
            return images[i];
807
        }
808
    }
809
    return 0;
810
}
811

    
812
// return machine entry from serverID
813
function get_machine(serverID) {
814
    for (i=0;i<servers.length;i++){
815
        if (servers[i]['id'] == serverID) {
816
            return servers[i];
817
        }
818
    }
819
    return 0;
820
}
821

    
822
// update the actions in icon view, per server
823
function update_iconview_actions(serverID, server_status) {
824
    if ($.cookie("view")=='2') {
825
        // remove .disable from all actions to begin with
826
        $('#machinesview-single #' + serverID + ' div.single-action').show();
827
        // decide which actions should be disabled
828
        for (current_action in actions) {
829
            if (actions[current_action].indexOf(server_status) == -1 ) {
830
                $('#machinesview-single #' + serverID + ' div.action-' + current_action).hide();
831
            }
832
        }
833
    } else {
834
        // remove .disable from all actions to begin with
835
        $('#machinesview-icon.standard #' + serverID + ' div.actions').find('a').removeClass('disabled');
836
        // decide which actions should be disabled
837
        for (current_action in actions) {
838
            if (actions[current_action].indexOf(server_status) == -1 ) {
839
                $('#machinesview-icon.standard #' + serverID + ' a.action-' + current_action).addClass('disabled');
840
            }
841
        }
842
    }
843
}
844

    
845
// update the actions in list view
846
function update_listview_actions() {
847
    var states = [];
848
    var on = [];
849
    var checked = $("table.list-machines tbody input[type='checkbox']:checked");
850
    // disable all actions to begin with
851
    $('#machinesview .list div.actions').children().removeClass('enabled');
852

    
853
    // are there multiple machines selected?
854
    if (checked.length>1)
855
        states[0] = 'multiple';
856

    
857
    // check the states of selected machines
858
    checked.each(function(i,checkbox) {
859
        states[states.length] = checkbox.className;
860
        var ip = $("#" + checkbox.id.replace('input-','') + ".ip span.public").text();
861
        if (ip.replace('undefined','').length)
862
            states[states.length] = 'network';
863
    });
864

    
865
    // decide which actions should be enabled
866
    for (a in actions) {
867
        var enabled = false;
868
        for (var s =0; s<states.length; s++) {
869
            if (actions[a].indexOf(states[s]) != -1 ) {
870
                enabled = true;
871
            } else {
872
                enabled = false;
873
                break;
874
            }
875
        }
876
        if (enabled)
877
            on[on.length]=a;
878
    }
879
    // enable those actions
880
    for (action in on) {
881
        $("#action-" + on[action]).addClass('enabled');
882
    }
883
}
884

    
885
//create server action
886
function create_vm(machineName, imageRef, flavorRef){
887
    var image_logo = os_icon(get_image(imageRef).metadata);
888
    var uri = API_URL + '/servers';
889
    var payload = {
890
        "server": {
891
            "name": machineName,
892
            "imageRef": imageRef,
893
            "flavorRef" : flavorRef,
894
            "metadata" : {
895
                "OS" : image_logo
896
            }
897
        }
898
    };
899

    
900
    $.ajax({
901
    url: uri,
902
    type: "POST",
903
    contentType: "application/json",
904
    dataType: "json",
905
    data: JSON.stringify(payload),
906
    timeout: TIMEOUT,
907
    error: function(jqXHR, textStatus, errorThrown) {
908
                // close wizard and show error box
909
                $('#machines-pane a#create').data('overlay').close();
910
                    try {
911
                        ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
912
                    } catch(err) {
913
                        ajax_error(-5, "UI Error", 'Create VM', err);
914
                    }
915
           },
916
    success: function(data, textStatus, jqXHR) {
917
                if ( jqXHR.status == '202') {
918
                    ajax_success("CREATE_VM_SUCCESS", data.server.adminPass);
919
                } else {
920
                    // close wizard and show error box
921
                    $('#machines-pane a#create').data('overlay').close();
922
                    ajax_error(jqXHR.status, undefined, 'Create VM', jqXHR.responseText);
923
                }
924
            }
925
    });
926
}
927

    
928
// reboot action
929
function reboot(serverIDs){
930
    if (!serverIDs.length){
931
        //ajax_success('DEFAULT');
932
        return false;
933
    }
934
    // ajax post reboot call
935
    var payload = {
936
        "reboot": {"type" : "HARD"}
937
    };
938

    
939
    var serverID = serverIDs.pop();
940

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

    
990
// shutdown action
991
function shutdown(serverIDs) {
992
    if (!serverIDs.length){
993
        //ajax_success('DEFAULT');
994
        return false;
995
    }
996
    // ajax post shutdown call
997
    var payload = {
998
        "shutdown": {}
999
    };
1000

    
1001
    var serverID = serverIDs.pop();
1002

    
1003
    $.ajax({
1004
        url: API_URL + '/servers/' + serverID + '/action',
1005
        type: "POST",
1006
        contentType: "application/json",
1007
        dataType: "json",
1008
        data: JSON.stringify(payload),
1009
        timeout: TIMEOUT,
1010
        error: function(jqXHR, textStatus, errorThrown) {
1011
                    try {
1012
                        display_failure(jqXHR.status, serverID, 'Shutdown', jqXHR.responseText);
1013
                    } catch(err) {
1014
                        display_failure(0, serverID, 'Shutdown', jqXHR.responseText);
1015
                    }
1016
                },
1017
        success: function(data, textStatus, jqXHR) {
1018
                    if ( jqXHR.status == '202') {
1019
                        try {
1020
                            console.info('suspended ' + serverID);
1021
                        } catch(err) {}
1022
                        // indicate that the action succeeded
1023
                        display_success(serverID);
1024
                        // continue with the rest of the servers
1025
                        shutdown(serverIDs);
1026
                    } else {
1027
                        ajax_error(jqXHR.status, serverID, 'Shutdown', jqXHR.responseText);
1028
                    }
1029
                }
1030
    });
1031
    return false;
1032
}
1033

    
1034
// destroy action
1035
function destroy(serverIDs) {
1036
    if (!serverIDs.length){
1037
        //ajax_success('DEFAULT');
1038
        return false;
1039
    }
1040
    // ajax post destroy call can have an empty request body
1041
    var payload = {};
1042

    
1043
    var serverID = serverIDs.pop();
1044

    
1045
    $.ajax({
1046
        url: API_URL + '/servers/' + serverID,
1047
        type: "DELETE",
1048
        contentType: "application/json",
1049
        dataType: "json",
1050
        data: JSON.stringify(payload),
1051
        timeout: TIMEOUT,
1052
        error: function(jqXHR, textStatus, errorThrown) {
1053
                    try {
1054
                        display_failure(jqXHR.status, serverID, 'Destroy', jqXHR.responseText);
1055
                    } catch(err) {
1056
                        display_failure(0, serverID, 'Destroy', jqXHR.responseText);
1057
                    }
1058
                },
1059
        success: function(data, textStatus, jqXHR) {
1060
                    if ( jqXHR.status == '204') {
1061
                        try {
1062
                            console.info('destroyed ' + serverID);
1063
                        } catch (err) {}
1064

    
1065
                        // update status on local storage object
1066
                        vm = get_machine(serverID);
1067
                        vm.status = "DESTROY";
1068

    
1069
                        // indicate that the action succeeded
1070
                        display_success(serverID);
1071
                        // continue with the rest of the servers
1072
                        destroy(serverIDs);
1073
                    } else {
1074
                        ajax_error(jqXHR.status, serverID, 'Destroy', jqXHR.responseText);
1075
                    }
1076
                }
1077
    });
1078
    return false;
1079
}
1080

    
1081
// start action
1082
function start(serverIDs){
1083
    if (!serverIDs.length){
1084
        //ajax_success('DEFAULT');
1085
        return false;
1086
    }
1087
    // ajax post start call
1088
    var payload = {
1089
        "start": {}
1090
    };
1091

    
1092
    var serverID = serverIDs.pop();
1093

    
1094
    $.ajax({
1095
        url: API_URL + '/servers/' + serverID + '/action',
1096
        type: "POST",
1097
        contentType: "application/json",
1098
        dataType: "json",
1099
        data: JSON.stringify(payload),
1100
        timeout: TIMEOUT,
1101
        error: function(jqXHR, textStatus, errorThrown) {
1102
                    try {
1103
                        display_failure(jqXHR.status, serverID, 'Start', jqXHR.responseText);
1104
                    } catch(err) {
1105
                        display_failure(0, serverID, 'Start', jqXHR.responseText);
1106
                    }
1107
                },
1108
        success: function(data, textStatus, jqXHR) {
1109
                    if ( jqXHR.status == '202') {
1110
                        try {
1111
                            console.info('started ' + serverID);
1112
                        } catch(err) {}
1113
                        // indicate that the action succeeded
1114
                        display_success(serverID);
1115
                        // continue with the rest of the servers
1116
                        start(serverIDs);
1117
                    } else {
1118
                        ajax_error(jqXHR.status, serverID, 'Start', jqXHR.responseText);
1119
                    }
1120
                }
1121
    });
1122
    return false;
1123
}
1124

    
1125
// Show VNC console
1126
function vnc_attachment(host, port, password) {
1127
    // FIXME: Must be made into parameters, in settings.py
1128
    //vnc = open("", "displayWindow",
1129
    //    "status=yes,toolbar=yes,menubar=yes");
1130
    vd = document.open("application/x-vnc");
1131

    
1132
    vd.writeln("[connection]");
1133
    vd.writeln("host=" + host);
1134
    vd.writeln("port=" + port);
1135
    vd.writeln("password=" + password);
1136

    
1137
    vd.close();
1138
}
1139

    
1140
// Show VNC console
1141
function show_vnc_console(serverID, serverName, serverIP, host, port, password) {
1142
    var params_url = '?machine=' + serverName + '&host_ip=' + serverIP + '&host=' + host + '&port=' + port + '&password=' + password;
1143
    var params_window = 'scrollbars=no,' +
1144
                        'menubar=no,' +
1145
                        'toolbar=no,' +
1146
                        'status=no,' +
1147
                        'top=0,' +
1148
                        'left=0,' +
1149
                        'height=' + screen.height + ',' +
1150
                        'width=' + screen.width + ',' +
1151
                        'fullscreen=yes';
1152

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

    
1155
    // Restore os icon in list view
1156
    osIcon = $('#'+serverID).parent().parent().find('.list-logo');
1157
    osIcon.attr('src',osIcon.attr('os'));
1158
    return false;
1159
}
1160

    
1161
// console action
1162
function open_console(serverIDs){
1163
    if (!serverIDs.length){
1164
        //ajax_success('DEFAULT');
1165
        return false;
1166
    }
1167
    // ajax post start call
1168
    var payload = {
1169
        "console": {"type": "vnc"}
1170
    };
1171

    
1172
    var serverID = serverIDs.pop();
1173

    
1174
    var machine = get_machine(serverID);
1175
    var serverName = machine.name;
1176
    try {
1177
        var serverIP = machine.addresses.values[0].values[0].addr;
1178
    } catch(err) { var serverIP = 'undefined'; }
1179

    
1180
    $.ajax({
1181
        url: API_URL + '/servers/' + serverID + '/action',
1182
        type: "POST",
1183
        contentType: "application/json",
1184
        dataType: "json",
1185
        data: JSON.stringify(payload),
1186
        timeout: TIMEOUT,
1187
        error: function(jqXHR, textStatus, errorThrown) {
1188
                    try {
1189
                        display_failure(jqXHR.status, serverID, 'Console', jqXHR.responseText);
1190
                    } catch(err) {
1191
                        display_failure(0, serverID, 'Console', jqXHR.responseText);
1192
                    }
1193
                },
1194
        success: function(data, textStatus, jqXHR) {
1195
                    if ( jqXHR.status == '200') {
1196
                        try {
1197
                            console.info('got_console ' + serverID);
1198
                        } catch(err) {}
1199
                        // indicate that the action succeeded
1200
                        // show_vnc_console(serverID, serverName, serverIP,
1201
                        // data.console.host,data.console.port,data.console.password);
1202
                        show_vnc_console(serverID, serverName, serverIP,
1203
                                         data.console.host,data.console.port,data.console.password);
1204
                        display_success(serverID);
1205
                        // hide spinner
1206
                        $('#' + serverID + ' .spinner').hide();
1207
                        // continue with the rest of the servers
1208
                        open_console(serverIDs);
1209
                    } else {
1210
                        ajax_error(jqXHR.status, serverID, 'Console', jqXHR.responseText);
1211
                    }
1212
                }
1213
    });
1214
    return false;
1215
}
1216

    
1217
function vm_has_address(vmId) {
1218
    var vm = get_machine(vmId);
1219

    
1220
    if (!vm) return false;
1221

    
1222
    try {
1223
        var ip = vm.addresses.values[0].values[0].addr;
1224
    } catch (err) {
1225
        return false;
1226
    }
1227
    return ip;
1228
}
1229

    
1230
// connect to machine action
1231
function machine_connect(serverIDs){
1232
    if (!serverIDs.length){
1233
        //ajax_success('DEFAULT');
1234
        return false;
1235
    }
1236

    
1237
    var serverID = serverIDs.pop();
1238
    var machine = get_machine(serverID);
1239
    var serverName = machine.name;
1240
    
1241
    try {
1242
        var serverIP = machine.addresses.values[0].values[0].addr;
1243
    } catch(err) { var serverIP = 'undefined'; }
1244

    
1245
    try {
1246
        var os = os_icon(machine.metadata);
1247
    } catch(err) { var os = 'undefined'; }
1248

    
1249
    var params_url = '?ip_address=' + serverIP + '&os=' + os + "&host_os=" + $.client.os + "&srv=" + serverID;
1250
    
1251
    if ($.client.os == "Windows" && os == "windows") {
1252
        window.open('machines/connect' + params_url + "&rdp=1");
1253
        return;
1254
    }
1255
    
1256
    var title = 'Connect to: ' + '<span class="machine-title"><img src="static/icons/machines/small/'+os+'-on.png" /> '+serverName+'</span>';
1257

    
1258
    try {
1259
        msg_box({title:title, content:'loading...',extra:'',
1260
        'ajax':'machines/connect' + params_url,
1261
        parse_data:function(data){
1262
            var box_content = "<a href='"+data.link.url+"'>"+data.link.title+"</a>";
1263
            if (!data.link.url) {
1264
                box_content = "<span class='cmd'>"+data.link.title+"</span>";
1265
            }
1266
            data.title = false;
1267
            data.content = data.info;
1268
            data.extra = box_content;
1269
            return data;
1270
        }
1271
        });
1272
    } catch (error) {
1273
        window.open('machines/connect' + params_url);
1274
    }
1275

    
1276

    
1277
    // Restore os icon in list view
1278
    osIcon = $('#'+serverID).parent().parent().find('.list-logo');
1279
    osIcon.attr('src',osIcon.attr('os'));
1280

    
1281
    return false;
1282
}
1283

    
1284

    
1285
// rename server
1286
function rename(serverID, serverName){
1287
    if (!serverID.length){
1288
        //ajax_success('DEFAULT');
1289
        return false;
1290
    }
1291
    // ajax post rename call
1292
    var payload = {
1293
        "server": {"name": serverName}
1294
    };
1295

    
1296
    $.ajax({
1297
        url: API_URL + '/servers/' + serverID,
1298
        type: "PUT",
1299
        contentType: "application/json",
1300
        dataType: "json",
1301
        data: JSON.stringify(payload),
1302
        timeout: TIMEOUT,
1303
        error: function(jqXHR, textStatus, errorThrown) {
1304
                    try {
1305
                        display_failure(jqXHR.status, serverID, 'Rename', jqXHR.responseText);
1306
                    } catch(err) {
1307
                        display_failure(0, serverID, 'Rename', jqXHR.responseText);
1308
                    }
1309
                },
1310
        success: function(data, textStatus, jqXHR) {
1311
                    if ( jqXHR.status == '204' || jqXHR.status == '1223') {
1312
                        try {
1313
                            console.info('renamed ' + serverID);
1314
                        } catch(err) {}
1315
                        // indicate that the action succeeded
1316
                        display_success(serverID);
1317
                    } else {
1318
                        ajax_error(jqXHR.status, serverID, 'Rename', jqXHR.responseText);
1319
                    }
1320
                }
1321
    });
1322
    return false;
1323
}
1324

    
1325
// get server metadata
1326
function get_metadata(serverID, keys_only) {
1327
    $.ajax({
1328
        url: API_URL + '/servers/' + serverID + '/meta',
1329
        cache: false,
1330
        type: "GET",
1331
        //async: false,
1332
        dataType: "json",
1333
        timeout: TIMEOUT,
1334
        error: function(jqXHR, textStatus, errorThrown) {
1335
            try {
1336
                // close wizard and show error box
1337
                $("a#metadata-scrollable").data('overlay').close();
1338
                ajax_error(jqXHR.status, undefined, 'Get metadata', jqXHR.responseText);
1339
            } catch (err) {
1340
                ajax_error(-5, "UI Error", "Get metadata", err);
1341
            }
1342
        },
1343
        success: function(data, textStatus, jqXHR) {
1344
            // to list the new results in the edit dialog
1345
            if (keys_only) {
1346
                list_metadata_keys(serverID, data.metadata.values);
1347
            } else {
1348
                list_metadata(data);
1349
                list_metadata_keys(serverID, data.metadata.values);
1350
            }
1351
            //hide spinner
1352
            $('#metadata-wizard .large-spinner').hide();
1353
        }
1354
    });
1355
    return false;
1356
}
1357

    
1358
// delete metadata key-value pair
1359
function delete_metadata(serverID, meta_key) {
1360
    $.ajax({
1361
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1362
        type: "DELETE",
1363
        //async: false,
1364
        dataType: "json",
1365
        timeout: TIMEOUT,
1366
        error: function(jqXHR, textStatus, errorThrown) {
1367
            try {
1368
                // close wizard and show error box
1369
                $("a#metadata-scrollable").data('overlay').close();
1370
                ajax_error(jqXHR.status, undefined, 'Delete metadata', jqXHR.responseText);
1371
            } catch (err) {
1372
                ajax_error(-5, "UI Error", "Delete metadata", err);
1373
            }
1374
        },
1375
        success: function(data, textStatus, jqXHR) {
1376
                    // success: Do nothing, the UI is already updated
1377
        }
1378
    });
1379
    return false;
1380
}
1381

    
1382
// add metadata key-value pair
1383
function update_metadata(serverID, meta_key, meta_value) {
1384
    var payload = {
1385
        "meta": {
1386
        }
1387
    };
1388
    payload["meta"][meta_key] = meta_value;
1389

    
1390
    $.ajax({
1391
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1392
        type: "PUT",
1393
        contentType: "application/json",
1394
        dataType: "json",
1395
        data: JSON.stringify(payload),
1396
        timeout: TIMEOUT,
1397
        error: function(jqXHR, textStatus, errorThrown) {
1398
            try {
1399
                // close wizard and show error box
1400
                $("a#metadata-scrollable").data('overlay').close();
1401
                ajax_error(jqXHR.status, undefined, 'add metadata', jqXHR.responseText);
1402
            } catch (err) {
1403
                ajax_error(-5, "UI Error", "add metadata", err);
1404
            }
1405
        },
1406
        success: function(data, textStatus, jqXHR) {
1407
            // success: Update icons if meta key is OS
1408
            if (meta_key == "OS") {
1409
                $("#metadata-wizard .machine-icon").attr("src","static/icons/machines/small/" + os_icon_from_value(meta_value) + '-' + $("#metadata-wizard div#on-off").text() + '.png');
1410
                var machine_icon = $("#machinesview-icon").find("div#" + serverID);
1411
                var machine_single = $("#machinesview-single").find("div#" + serverID);
1412

    
1413
                var os = os_icon_from_value(meta_value);
1414
                var state = $("#metadata-wizard div#on-off").text()
1415
                var state_single = $(".state", machine_single).hasClass("terminated-state") ? "off" : "on";
1416
                set_machine_os_image(machine_icon, "icon", state, os);
1417
                set_machine_os_image(machine_single, "single", state_single, os);
1418
            }
1419
        }
1420
    });
1421
    return false;
1422
}
1423

    
1424
// get stats
1425
function get_server_stats(serverID) {
1426
    $.ajax({
1427
        url: API_URL + '/servers/' + serverID + '/stats',
1428
        cache: false,
1429
        type: "GET",
1430
        //async: false,
1431
        dataType: "json",
1432
        timeout: TIMEOUT,
1433
        error: function(jqXHR, textStatus, errorThrown) {
1434
                // report error as text inline
1435
                $('#' + serverID + ' img.busy').hide();
1436
                $('#' + serverID + ' div.stat-error').show();
1437
        },
1438
        success: function(data, textStatus, jqXHR) {
1439
            // in icon view
1440
            if ( $.cookie('view') == 0 ) {
1441
                $('#' + serverID + ' img.busy').removeClass('busy');
1442
                $('#' + serverID + ' img.cpu').attr("src", data.stats.cpuBar);
1443
                $('#' + serverID + ' img.net').attr("src", data.stats.netBar);
1444
            }
1445
            // in single view
1446
            else if ( $.cookie('view') == 2 ) {
1447
                $('#' + serverID + ' div.cpu-graph img.stats').attr("src", data.stats.cpuTimeSeries);
1448
                $('#' + serverID + ' div.network-graph img.stats').attr("src", data.stats.netTimeSeries);
1449
            }
1450
        }
1451
    });
1452
    return false;
1453
}
1454

    
1455
// create network
1456
function create_network(networkName){
1457
    // ajax post start call
1458
    var payload = {
1459
        "network": { "name": networkName }
1460
    };
1461

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

    
1499
// rename network
1500
function rename_network(networkID, networkName){
1501
    if (!networkID.length){
1502
        //ajax_success('DEFAULT');
1503
        return false;
1504
    }
1505
    // prepare payload
1506
    var payload = {
1507
        "network": {"name": networkName}
1508
    };
1509
    // ajax call
1510
    $.ajax({
1511
        url: API_URL + '/networks/' + networkID,
1512
        type: "PUT",
1513
        contentType: "application/json",
1514
        dataType: "json",
1515
        data: JSON.stringify(payload),
1516
        timeout: TIMEOUT,
1517
        error: function(jqXHR, textStatus, errorThrown) {
1518
            try {
1519
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1520
            } catch (err) {
1521
                ajax_error(-1, "UI Error", 'Rename network', err);
1522
            }
1523
        },
1524
        success: function(data, textStatus, jqXHR) {
1525
            if ( jqXHR.status == '204') {
1526
                try {
1527
                    console.info('renamed network' + networkID);
1528
                } catch(err) {}
1529
            } else {
1530
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1531
            }
1532
        }
1533
    });
1534
    return false;
1535
}
1536

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

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

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

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

    
1721
// show the welcome screen
1722
function showWelcome() {
1723
    $("#view-select").fadeOut("fast");
1724
    $("#emptymachineslist").fadeIn("fast");
1725
    $("#machinesview").hide();
1726
}
1727

    
1728
// hide the welcome screen
1729
function hideWelcome() {
1730
    $("#emptymachineslist").fadeOut("fast");
1731
    $("#view-select").fadeIn("fast");
1732
    $("div#view-select").show();
1733
    $("#machinesview").show();
1734
}
1735

    
1736
function log_server_status_change(server_entry, new_status) {
1737
    // firebug console logging
1738
    try {
1739
        if ($("#machinesview-single").length > 0) {
1740
            console.info(server_entry.find("div.machine-details div.name").text() +
1741
                        ' from ' + server_entry.find(".state-label").text() +
1742
                        ' to ' + STATUSES[new_status]);
1743
        } else {
1744
            console.info(server_entry.find("div.name span.name").text() +
1745
                        ' from ' + server_entry.find(".status").text() +
1746
                        ' to ' + STATUSES[new_status]);
1747
        }
1748
    } catch(err) {}
1749
}
1750

    
1751
function get_flavor_params(flavorRef) {
1752
    var cpus, ram, disk;
1753
    if ( flavors.length > 0 ) {
1754
        var current_flavor = '';
1755
        for (i=0; i<flavors.length; i++) {
1756
            if (flavors[i]['id'] == flavorRef) {
1757
                current_flavor = flavors[i];
1758
            }
1759
        }
1760
        cpus = current_flavor['cpu'];
1761
        ram = current_flavor['ram'];
1762
        disk = current_flavor['disk'];
1763
    } else {
1764
        cpus = 'undefined';
1765
        ram = 'undefined';
1766
        disk = 'undefined';
1767
    }
1768
    return {'cpus': cpus, 'ram': ram, 'disk': disk};
1769
}
1770

    
1771
function get_image_params(imageRef) {
1772
    var image_name, image_size;
1773
    if ( images.length > 0 ) {
1774
        var current_image = '';
1775
        for (i=0; i<images.length; i++) {
1776
            if (images[i]['id'] == imageRef) {
1777
                current_image = images[i];
1778
            }
1779
        }
1780
        try {
1781
            image_name = current_image['name'];
1782
        } catch(err) { image_name = 'undefined'; }
1783
        try{
1784
            image_size = current_image['metadata']['values']['size'];
1785
        } catch(err) { image_size = 'undefined'; }
1786
    } else {
1787
        image_name = 'undefined';
1788
        image_size = 'undefined';
1789
    }
1790
    return {'name': image_name,'size': image_size};
1791
}
1792

    
1793
function get_public_ips(server) {
1794
    var ip4, ip6;
1795
    try {
1796
        if (server.addresses.values) {
1797
            $.each (server.addresses.values, function(i, value) {
1798
                if (value.id == 'public') {
1799
                    try {
1800
                        $.each (value.values, function(i, ip) {
1801
                            if (ip.version == '4') {
1802
                                ip4 = ip.addr;
1803
                            } else if (ip.version == '6') {
1804
                                ip6 = ip.addr;
1805
                            } else {
1806
                                ip4 = 'pending';
1807
                                ip6 = 'pending';
1808
                            }
1809
                        });
1810
                    } catch (err){
1811
                        try{console.info('Server ' + server.id + ' has invalid ips')}catch(err){};
1812
                        ip4 = 'pending';
1813
                        ip6 = 'pending';
1814
                    }
1815
                }
1816
            });
1817
        }
1818
    } catch (err) {
1819
        try{console.info('Server ' + server.id + ' has no network addresses')}catch(err){};
1820
        ip4 = 'pending';
1821
        ip6 = 'pending';
1822
    }
1823
    return {'ip4': ip4, 'ip6': ip6};
1824
}
1825

    
1826
function get_private_ips(server) {
1827

    
1828
}
1829

    
1830
function close_all_overlays() {
1831
        try {
1832
                $("a#networkscreate").overlay().close();
1833
        } catch(err) {}
1834
        try {
1835
                $('a#create').overlay().close();
1836
        } catch(err) {}
1837
        try {
1838
                $("a#add-machines-overlay").overlay().close();
1839
        } catch(err) {}
1840
        try {
1841
                $("a#metadata-scrollable").overlay().close();
1842
        } catch(err) {}
1843
        try {
1844
                $("a#msgbox").overlay().close();
1845
        } catch(err) {}
1846
        try {
1847
                $("a#feedbackbox").overlay().close();
1848
        } catch(err) {}
1849
}
1850

    
1851
// logout
1852
function user_session_logout() {
1853
    $.cookie("X-Auth-Token", null);
1854
    if (window.LOGOUT_REDIRECT !== undefined)
1855
    {
1856
        window.location = window.LOGOUT_REDIRECT;
1857
    } else {
1858
        window.location.reload();
1859
    }
1860
}
1861

    
1862
// action indicators
1863
function init_action_indicator_handlers(machines_view)
1864
{
1865
    // init once for each view
1866
    if (window.ACTION_ICON_HANDLERS == undefined)
1867
    {
1868
        window.ACTION_ICON_HANDLERS = {};
1869
    }
1870

    
1871
    if (machines_view in window.ACTION_ICON_HANDLERS)
1872
    {
1873
        return;
1874
    }
1875
    window.ACTION_ICON_HANDLERS[machines_view] = 1;
1876

    
1877
    if (machines_view == "list")
1878
    {
1879
        // totally different logic for list view
1880
        init_action_indicator_list_handlers();
1881
        return;
1882
    }
1883

    
1884
    function update_action_icon_indicators(force)
1885
    {
1886
        function show(el, action) {
1887
            $(".action-indicator", $(el)).attr("class", "action-indicator " + action);
1888
            $(".action-indicator", $(el)).show();
1889
        }
1890

    
1891
        function hide(el) {
1892
            $(".action-indicator", $(el)).hide();
1893
        }
1894

    
1895
        function get_pending_actions(el) {
1896
            return $(".confirm_single:visible", $(el));
1897
        }
1898

    
1899
        function other_indicators(el) {
1900
           return $("img.wave:visible, img.spinner:visible", $(el))
1901
        }
1902

    
1903
        $("div.machine:visible, div.single-container").each(function(index, el){
1904
            var el = $(el);
1905
            var pending = get_pending_actions(el);
1906
            var other = other_indicators(el);
1907
            var action = undefined;
1908
            var force_action = force;
1909
            var visible = $(el).css("display") == "block";
1910

    
1911
            if (force_action !==undefined && force_action.el !== el[0]) {
1912
                // force action for other vm
1913
                // skipping force action
1914
                force_action = undefined;
1915
            }
1916

    
1917
            if (force_action !==undefined && force_action.el === el[0]) {
1918
                action = force_action.action;
1919
            }
1920

    
1921
            if (other.length >= 1) {
1922
                return;
1923
            }
1924

    
1925
            if (pending.length >= 1 && force_action === undefined) {
1926
                action = $(pending.parent()).attr("class").replace("action-container","");
1927
            }
1928

    
1929
            if (action in {'console':''}) {
1930
                return;
1931
            }
1932

    
1933
            if (action !== undefined) {
1934
                show(el, action);
1935
            } else {
1936
                try {
1937
                    if (el.attr('id') == pending_actions[0][1])
1938
                    {
1939
                        return;
1940
                    }
1941
                } catch (err) {
1942
                }
1943
                hide(el);
1944
            }
1945

    
1946
        });
1947
    }
1948

    
1949
    // action indicators
1950
    $(".action-container").live('mouseover', function(evn) {
1951
        force_action = {'el': $(evn.currentTarget).parent().parent()[0], 'action':$(evn.currentTarget).attr("class").replace("action-container","")};
1952
        // single view case
1953
        if ($(force_action.el).attr("class") == "upper")
1954
        {
1955
            force_action.el = $(evn.currentTarget).parent().parent().parent()[0]
1956
        };
1957
        update_action_icon_indicators(force_action);
1958
    });
1959

    
1960
    $("img.spinner, img.wave").live('hide', function(){
1961
        update_action_icon_indicators();
1962
    });
1963
    // register events where icons should get updated
1964

    
1965
    // hide action indicator image on mouse out, spinner appear, wave appear
1966
    $(".action-container").live("mouseout", function(evn){
1967
        update_action_icon_indicators();
1968
    });
1969

    
1970
    $(".confirm_single").live("click", function(evn){
1971
        update_action_icon_indicators();
1972
    });
1973

    
1974
    $("img.spinner, img.wave").live('show', function(){
1975
        $("div.action-indicator").hide();
1976
    });
1977

    
1978
    $(".confirm_single button.no").live('click', function(evn){
1979
        $("div.action-indicator", $(evn.currentTarget).parent().parent()).hide();
1980
    });
1981

    
1982
    $(".confirm_multiple button.no").click(function(){
1983
        $("div.action-indicator").hide();
1984
    });
1985

    
1986
    $(".confirm_multiple button.yes").click(function(){
1987
        $("div.action-indicator").hide();
1988
    });
1989
}
1990

    
1991
function init_action_indicator_list_handlers()
1992
{
1993
    var skip_actions = { 'connect':'','details':'' };
1994

    
1995
    var has_pending_confirmation = function()
1996
    {
1997
        return $(".confirm_multiple:visible").length >= 1
1998
    }
1999

    
2000
    function update_action_indicator_icons(force_action, skip_pending)
2001
    {
2002
        // pending action based on the element class
2003
        var pending_action = $(".selected", $(".actions"))[0];
2004
        var selected = get_list_view_selected_machine_rows();
2005

    
2006
        // reset previous state
2007
        list_view_hide_action_indicators();
2008

    
2009
        if (pending_action == undefined && !force_action)
2010
        {
2011
            // no action selected
2012
            return;
2013
        }
2014

    
2015
        if (force_action != undefined)
2016
        {
2017
            // user forced action choice
2018
            var action_class = force_action;
2019
        } else {
2020
            // retrieve action name (reboot, stop, etc..)
2021
            var action_class = $(pending_action).attr("id").replace("action-","");
2022
        }
2023

    
2024
        selected.each(function(index, el) {
2025
            if (has_pending_confirmation() && skip_pending)
2026
            {
2027
                return;
2028
            }
2029
            var el = $(el);
2030
            var logo = $("img.list-logo", el);
2031
            $(".action-indicator", el).remove();
2032
            var cls = "action-indicator " + action_class;
2033
            // add icon div
2034
            logo.after('<div class="' + cls + '"></div>');
2035
            // hide os logo
2036
            $("img.list-logo", el).hide();
2037
        });
2038
    }
2039

    
2040
    // on mouseover we force the images to the hovered action
2041
    $(".actions a").live("mouseover", function(evn) {
2042
        var el = $(evn.currentTarget);
2043
        if (!el.hasClass("enabled"))
2044
        {
2045
            return;
2046
        }
2047
        var action_class = el.attr("id").replace("action-","");
2048
        if (action_class in skip_actions)
2049
        {
2050
            return;
2051
        }
2052
        update_action_indicator_icons(action_class, false);
2053
    });
2054

    
2055

    
2056
    // register events where icons should get updated
2057
    $(".actions a.enabled").live("click", function(evn) {
2058
        // clear previous selections
2059
        $("a.selected").removeClass("selected");
2060

    
2061
        var el = $(evn.currentTarget);
2062
        el.addClass("selected");
2063
        update_action_indicator_icons(undefined, false);
2064
    });
2065

    
2066
    $(".actions a").live("mouseout", function(evn) {
2067
        update_action_indicator_icons(undefined, false);
2068
    });
2069

    
2070
    $(".confirm_multiple button.no").click(function(){
2071
        list_view_hide_action_indicators();
2072
    });
2073

    
2074
    $(".confirm_multiple button.yes").click(function(){
2075
        list_view_hide_action_indicators();
2076
    });
2077

    
2078
    $("input[type=checkbox]").live('change', function(){
2079
        // pending_actions will become empty on every checkbox click/change
2080
        // line 154 machines_list.html
2081
        pending_actions = [];
2082
        if (pending_actions.length == 0)
2083
        {
2084
            $(".confirm_multiple").hide();
2085
            $("a.selected").each(function(index, el){$(el).removeClass("selected")});
2086
        }
2087
        update_action_indicator_icons(undefined, false);
2088
    });
2089

    
2090
}
2091

    
2092
function list_view_hide_action_indicators()
2093
{
2094
    $("tr td .action-indicator").remove();
2095
    $("tr td img.list-logo").show();
2096
}
2097

    
2098
function get_list_view_selected_machine_rows()
2099
{
2100
    var table = $("table.list-machines");
2101
    var rows = $("tr:has(input[type=checkbox]:checked)",table);
2102
    return rows;
2103
}
2104

    
2105
// machines images utils
2106
function set_machine_os_image(machine, machines_view, state, os, skip_reset_states, remove_state) {
2107
    var views_map = {'single': '.single-image', 'icon': '.logo'};
2108
    var states_map = {'on': 'state1', 'off': 'state3', 'hover': 'state4', 'click': 'state2'}
2109
    var sizes_map = {'single': 'large', 'icon': 'medium'}
2110

    
2111
    var size = sizes_map[machines_view];
2112
    var img_selector = views_map[machines_view];
2113
    var cls = states_map[state];
2114

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

    
2118
    var el = $(img_selector, machine);
2119
    var current_img = el.css("backgroundImage");
2120
    if (os == undefined){
2121
        new_img = current_img;
2122
    }
2123

    
2124
    // os changed
2125
    el.css("backgroundImage", new_img);
2126

    
2127
    // reset current state
2128
    if (skip_reset_states === undefined)
2129
    {
2130
        el.removeClass("single-image-state1");
2131
        el.removeClass("single-image-state2");
2132
        el.removeClass("single-image-state3");
2133
        el.removeClass("single-image-state4");
2134
    }
2135

    
2136
    if (remove_state !== undefined)
2137
    {
2138
        remove_state = "single-image-" + states_map[remove_state];
2139
        el.removeClass(remove_state);
2140
        return;
2141
    }
2142
    
2143
    // set proper state
2144
    el.addClass("single-image-" + cls);
2145
}
2146

    
2147

    
2148
// generic info box
2149
function show_feedback_form(msg, from_error) {
2150
    var box = $("#feedback-form");
2151
    box.addClass("notification-box");
2152

    
2153
    // initialize
2154
    box.find(".form-container").show();
2155
    box.find("textarea").val("");
2156
    box.find(".message").hide();
2157
    
2158
    var initial_msg = msg || undefined;
2159
    
2160
    var triggers = $("a#feedbackbox").overlay({
2161
        // some mask tweaks suitable for modal dialogs
2162
        mask: '#666',
2163
        top: '10px',
2164
        fixed: false,
2165
        closeOnClick: false,
2166
        oneInstance: false,
2167
        load: false
2168
    });
2169

    
2170
    
2171
    if (initial_msg && from_error) {
2172
        // feedback form from ajax_error window
2173
        box.find("textarea").val(initial_msg);
2174
        $("a#feedbackbox").overlay().onClose(function(){window.location.reload()});
2175
        box.find("textarea").height(200);
2176
        $("a#feedbackbox").overlay().onLoad(function(){box.find("textarea").focus().setCursorPosition(500);});
2177
        
2178
    }
2179

    
2180
    $("#feedback-form form").unbind("submit");
2181
    $("#feedback-form form").submit(function(event) {
2182
        event.preventDefault();
2183
            
2184
        // empty msg
2185
        if ($("textarea.feedback-text").val().replace(/^\s*|\s*$/,"") == "") {
2186
            alert($(".empty-error-msg", this).text());
2187
            return;
2188
        }
2189

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

    
2192
        $.ajax({
2193
            url: FEEDBACK_URL,
2194
            data: $(this).serialize(),
2195
            type: "POST",
2196
            // show loading
2197
            beforeSend: function() {box.find(".form-container").hide(); box.find(".sending").fadeIn() },
2198
            // hide form
2199
            complete: function() { box.find(".form-container").hide(); box.find(".sending").hide() },
2200
            // on success display success message
2201
            success: function() { box.find(".success").fadeIn(); box.find(".sending").hide() },
2202
            // display error message
2203
            error: function() { box.find(".errormsg").fadeIn(); box.find(".sending").hide() }
2204
        })
2205
    });
2206
    
2207
    $("a#feedbackbox").data('overlay').load();
2208

    
2209
    // reset feedback_pending for ajax_errors
2210
    window.FEEDBACK_PENDING = false;
2211
    return false;
2212
}
2213

    
2214
function get_user_data(extra_data) {
2215
    return $.extend({
2216
        'servers': $.extend({}, servers),
2217
        'client': {'browser': $.browser, 'screen': $.extend({}, screen), 'client': $.client},
2218
        'dates': {'now': new Date, 'lastUpdate': changes_since_date}
2219
    }, extra_data);
2220
}
2221

    
2222
function msg_box(config) {
2223
    var config = $.extend({'title':'Info message', 'content': 'this is an info message', 'ajax': false, 'extra':false}, config);
2224
    // prepare the error message
2225
    // bring up success notification
2226

    
2227
    var box = $("#notification-box");
2228
    box.addClass("notification-box");
2229
    box.addClass('success');
2230
    box.removeClass('error');
2231

    
2232
    var sel = function(s){return $(s, box)};
2233
    // reset texts
2234
    sel("h3 span.header-box").html("");
2235
    sel(".sub-text").html("");
2236
    sel(".password-container .password").html("");
2237
    sel("div.machine-now-building").html("");
2238
    
2239

    
2240
    // apply msg box contents
2241
    sel("h3 span.header-box").html(config.title);
2242
    sel("div.machine-now-building").html(config.content);
2243
    sel(".popup-header").removeClass("popup-header-error");
2244
    box.removeClass("popup-border-error");
2245
    sel(".popup-details").removeClass("popup-details-error");
2246
    sel(".popup-separator").removeClass("popup-separator-error");
2247
    
2248
    sel(".password-container").hide();
2249
    if (config.extra) {
2250
        sel(".password-container .password").html(config.extra);
2251
        sel(".password-container").show();
2252
    }
2253

    
2254
    var triggers = $("a#msgbox").overlay({
2255
        // some mask tweaks suitable for modal dialogs
2256
        mask: '#666',
2257
        top: '10px',
2258
        closeOnClick: false,
2259
        oneInstance: false,
2260
        load: false,
2261
        fixed: false,
2262
        onClose: function () {
2263
            // With partial refresh working properly,
2264
            // it is no longer necessary to refresh the whole page
2265
            // choose_view();
2266
        }
2267
    });
2268
    $("a#msgbox").data('overlay').load();
2269
    
2270
    var parse_data = config.parse_data || false;
2271
    var load_html = config.html || false;
2272
    var user_success = config.success || false;
2273
    config.ajax = config.ajax || {};
2274

    
2275
    // requested to show remote data in msg_box
2276
    if (config.ajax) {
2277
        $.ajax($.extend({ 
2278
            url:config.ajax, 
2279
            success: function(data){
2280
                // we want to get our data parsed before
2281
                // placing them in content
2282
                if (parse_data) {
2283
                    data = parse_data(data);
2284
                }
2285

    
2286
                // no json response
2287
                // load html body
2288
                if (load_html) {
2289
                    sel("div.machine-now-building").html(data);
2290
                } else {
2291

    
2292
                    if (data.title) {
2293
                        sel("h3 span.header-box").text(data.title);
2294
                    }
2295

    
2296
                    if (data.content) {
2297
                        sel("div.machine-now-building").html(data.content);
2298
                    }
2299
                    if (data.extra) {
2300
                        sel(".password-container .password").html(data.extra);
2301
                        sel(".password-container").show();
2302
                    }
2303
                    if (data.subinfo) {
2304
                        sel(".sub-text").html(data.subinfo);
2305
                    } else {
2306
                        sel(".sub-text").html("");
2307
                    }
2308
                }
2309

    
2310
                if (user_success) {
2311
                    user_success($("div.machine-now-building"));
2312
                }
2313
            },
2314
            error: function(xhr, status, err) {
2315
                ajax_error(-5, "UI Error", "Machine connect", err);
2316
            }
2317
        }, config.ajax_config));
2318
    }
2319
    return false;
2320
}
2321

    
2322

    
2323
function show_invitations() {
2324

    
2325
    handle_invitations = function(el) {
2326
        el.addClass("invitations");
2327
        var cont = el;
2328
        var form = $(el).find("form");
2329

    
2330
        // remove garbage rows that stay in DOM between requests
2331
        $(".removable-field-row:hidden").remove();
2332
        $("#invform #removable-name-container-1").dynamicField();
2333
        
2334
        $(".invitations-left").hide();
2335
        $("#notification-box .header-box").html("");
2336
        $("#notification-box .header-box").html(window.INVITATIONS_TITLE + " " + $($(".invitations-left")[0]).text());
2337

    
2338
        form.submit(function(evn){
2339
            evn.preventDefault();
2340
            $.post(form.attr("action"), form.serialize(), function(data) {
2341
                $(cont).html(data); 
2342
                handle_invitations(cont);
2343
            });
2344
            return false;
2345
        });
2346
    }
2347

    
2348
    msg_box({title:window.INVITATIONS_TITLE, content:'', ajax:INVITATIONS_URL, html:true, success: function(el){ 
2349
        handle_invitations(el)}
2350
    });
2351
}
2352