Statistics
| Branch: | Tag: | Revision:

root / ui / static / synnefo.js @ f277a1cd

History | View | Annotate | Download (80.2 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.v4 + '&host_ip_v6=' + serverIP.v6 + '&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
    var url = 'machines/console' + params_url;
1154
    window.open(url, 'formresult' + serverID, params_window);
1155

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

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

    
1173
    var serverID = serverIDs.pop();
1174

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

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

    
1220
function vm_has_address(vmId) {
1221
    var vm = get_machine(vmId);
1222

    
1223
    if (!vm) return false;
1224

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

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

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

    
1248
    try {
1249
        var os = os_icon(machine.metadata);
1250
    } catch(err) { var os = 'undefined'; }
1251

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

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

    
1279

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

    
1284
    return false;
1285
}
1286

    
1287

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1829
function get_private_ips(server) {
1830

    
1831
}
1832

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

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

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

    
1874
    if (machines_view in window.ACTION_ICON_HANDLERS)
1875
    {
1876
        return;
1877
    }
1878
    window.ACTION_ICON_HANDLERS[machines_view] = 1;
1879

    
1880
    if (machines_view == "list")
1881
    {
1882
        // totally different logic for list view
1883
        init_action_indicator_list_handlers();
1884
        return;
1885
    }
1886

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

    
1894
        function hide(el) {
1895
            $(".action-indicator", $(el)).hide();
1896
        }
1897

    
1898
        function get_pending_actions(el) {
1899
            return $(".confirm_single:visible", $(el));
1900
        }
1901

    
1902
        function other_indicators(el) {
1903
           return $("img.wave:visible, img.spinner:visible", $(el))
1904
        }
1905

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

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

    
1920
            if (force_action !==undefined && force_action.el === el[0]) {
1921
                action = force_action.action;
1922
            }
1923

    
1924
            if (other.length >= 1) {
1925
                return;
1926
            }
1927

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

    
1932
            if (action in {'console':''}) {
1933
                return;
1934
            }
1935

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

    
1949
        });
1950
    }
1951

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

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

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

    
1973
    $(".confirm_single").live("click", function(evn){
1974
        update_action_icon_indicators();
1975
    });
1976

    
1977
    $("img.spinner, img.wave").live('show', function(){
1978
        $("div.action-indicator").hide();
1979
    });
1980

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

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

    
1989
    $(".confirm_multiple button.yes").click(function(){
1990
        $("div.action-indicator").hide();
1991
    });
1992
}
1993

    
1994
function init_action_indicator_list_handlers()
1995
{
1996
    var skip_actions = { 'connect':'','details':'' };
1997

    
1998
    var has_pending_confirmation = function()
1999
    {
2000
        return $(".confirm_multiple:visible").length >= 1
2001
    }
2002

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

    
2009
        // reset previous state
2010
        list_view_hide_action_indicators();
2011

    
2012
        if (pending_action == undefined && !force_action)
2013
        {
2014
            // no action selected
2015
            return;
2016
        }
2017

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

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

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

    
2058

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

    
2064
        var el = $(evn.currentTarget);
2065
        el.addClass("selected");
2066
        update_action_indicator_icons(undefined, false);
2067
    });
2068

    
2069
    $(".actions a").live("mouseout", function(evn) {
2070
        update_action_indicator_icons(undefined, false);
2071
    });
2072

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

    
2077
    $(".confirm_multiple button.yes").click(function(){
2078
        list_view_hide_action_indicators();
2079
    });
2080

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

    
2093
}
2094

    
2095
function list_view_hide_action_indicators()
2096
{
2097
    $("tr td .action-indicator").remove();
2098
    $("tr td img.list-logo").show();
2099
}
2100

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

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

    
2114
    var size = sizes_map[machines_view];
2115
    var img_selector = views_map[machines_view];
2116
    var cls = states_map[state];
2117

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

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

    
2127
    // os changed
2128
    el.css("backgroundImage", new_img);
2129

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

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

    
2150

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

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

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

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

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

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

    
2212
    // reset feedback_pending for ajax_errors
2213
    window.FEEDBACK_PENDING = false;
2214
    return false;
2215
}
2216

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

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

    
2230
    var box = $("#notification-box");
2231
    box.addClass("notification-box");
2232
    box.addClass('success');
2233
    box.removeClass('error');
2234

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

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

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

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

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

    
2295
                    if (data.title) {
2296
                        sel("h3 span.header-box").text(data.title);
2297
                    }
2298

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

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

    
2325

    
2326
function show_invitations() {
2327

    
2328
    handle_invitations = function(el) {
2329
        el.addClass("invitations");
2330
        var cont = el;
2331
        var form = $(el).find("form");
2332

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

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

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

    
2356

    
2357
function get_short_v6(v6, parts_to_keep) {
2358
    var parts = v6.split(":");
2359
    var new_parts = parts.slice(parts.length - parts_to_keep);
2360
    return new_parts.join(":");
2361
}
2362

    
2363
function fix_v6_addresses() {
2364

    
2365
    // what to prepend
2366
    var match = "... ";
2367
    // long ip min length
2368
    var limit = 20;
2369
    // parts to show after the transformation
2370
    // (from the end)
2371
    var parts_to_keep_from_end = 4;
2372

    
2373
    $(".ipv6-text").each(function(index, el){
2374
        var el = $(el);
2375
        var ip = $(el).text();
2376
            
2377
        // transformation not applyied
2378
        // FIXME: use $.data for the condition
2379
        if (ip.indexOf(match) == -1 && ip != "pending") {
2380
            
2381
            // only too long ips
2382
            if (ip.length > 20) {
2383
                $(el).data("ipstring", ip);
2384
                $(el).text(match + get_short_v6(ip, parts_to_keep_from_end));
2385
                $(el).attr("title", ip);
2386
                $(el).tooltip();
2387
            }
2388
        } else {
2389
            if (ip.indexOf(match) == 0) {
2390
            } else {
2391
                // not a long ip anymore
2392
                $(el).data("ipstring", undefined);
2393
                $(el).css({'text-decoration':'none'});
2394

    
2395
                if ($(el).data('tooltip')) {
2396
                    $(el).data('tooltip').show = function () {};
2397
                }
2398
            }
2399
        }
2400
    });
2401
}
2402