Statistics
| Branch: | Tag: | Revision:

root / ui / static / synnefo.js @ d0e3f226

History | View | Annotate | Download (82 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
    // prefer metadata values for specific options (username, domain)
1241
    var username_meta_key = 'User';
1242
    var domain_meta_key = "Domain";
1243

    
1244
    var serverID = serverIDs.pop();
1245
    var machine = get_machine(serverID);
1246
    var serverName = machine.name;
1247
    
1248
    try {
1249
        var serverIP = machine.addresses.values[0].values[0].addr;
1250
    } catch (err) { var serverIP = 'undefined'; }
1251

    
1252
    try {
1253
        var os = os_icon(machine.metadata);
1254
    } catch (err) { var os = 'undefined'; }
1255

    
1256
    var username = "";
1257
    try {
1258
        username = machine.metadata.values[username_meta_key];
1259
    } catch (err) { username = undefined }
1260

    
1261
    var domain = "";
1262
    try {
1263
        domain = machine.metadata.values[domain_meta_key];
1264
    } catch (erro) { domain = undefined }
1265

    
1266
    var params_url = '?ip_address=' + serverIP + '&os=' + os + "&host_os=" + $.client.os + "&srv=" + serverID;
1267

    
1268
    if (username) {
1269
        params_url += "&username=" + username;
1270
    }
1271

    
1272
    if (domain) {
1273
        params_url += "&domain=" + domain;
1274
    }
1275
    
1276
    if ($.client.os == "Windows" && os == "windows") {
1277
        // request rdp file
1278
        window.open('machines/connect' + params_url + "&rdp=1");
1279
        return;
1280
    }
1281
    
1282
    // FIXME: I18n ???
1283
    var title = 'Connect to: ' + '<span class="machine-title"><img src="static/icons/machines/small/'+os+'-on.png" /> '+serverName+'</span>';
1284
    
1285
    // open msg box and fill it with json data retrieved from connect machine view
1286
    try {
1287
        // open msg box
1288
        msg_box({
1289
            title:title, 
1290
            content:'loading...',
1291
            extra:'', 'ajax':'machines/connect' + params_url,
1292
            parse_data:function(data){
1293
                var box_content = "<a href='"+data.link.url+"'>"+data.link.title+"</a>";
1294
                if (!data.link.url) {
1295
                    box_content = "<span class='cmd'>"+data.link.title+"</span>";
1296
                }
1297
                data.title = false;
1298
                data.content = data.info;
1299
                data.extra = box_content;
1300
                return data;
1301
            }
1302
        });
1303
    } catch (error) {
1304
        // if msg box fails fallback redirecting the user to the connect url
1305
        window.open('machines/connect' + params_url);
1306
    }
1307

    
1308

    
1309
    // Restore os icon in list view
1310
    osIcon = $('#'+serverID).parent().parent().find('.list-logo');
1311
    osIcon.attr('src',osIcon.attr('os'));
1312

    
1313
    return false;
1314
}
1315

    
1316

    
1317
// rename server
1318
function rename(serverID, serverName){
1319
    if (!serverID.length){
1320
        //ajax_success('DEFAULT');
1321
        return false;
1322
    }
1323
    // ajax post rename call
1324
    var payload = {
1325
        "server": {"name": serverName}
1326
    };
1327

    
1328
    $.ajax({
1329
        url: API_URL + '/servers/' + serverID,
1330
        type: "PUT",
1331
        contentType: "application/json",
1332
        dataType: "json",
1333
        data: JSON.stringify(payload),
1334
        timeout: TIMEOUT,
1335
        error: function(jqXHR, textStatus, errorThrown) {
1336
                    try {
1337
                        display_failure(jqXHR.status, serverID, 'Rename', jqXHR.responseText);
1338
                    } catch(err) {
1339
                        display_failure(0, serverID, 'Rename', jqXHR.responseText);
1340
                    }
1341
                },
1342
        success: function(data, textStatus, jqXHR) {
1343
                    if ( jqXHR.status == '204' || jqXHR.status == '1223') {
1344
                        try {
1345
                            console.info('renamed ' + serverID);
1346
                        } catch(err) {}
1347
                        // indicate that the action succeeded
1348
                        display_success(serverID);
1349
                    } else {
1350
                        ajax_error(jqXHR.status, serverID, 'Rename', jqXHR.responseText);
1351
                    }
1352
                }
1353
    });
1354
    return false;
1355
}
1356

    
1357
// get server metadata
1358
function get_metadata(serverID, keys_only) {
1359
    $.ajax({
1360
        url: API_URL + '/servers/' + serverID + '/meta',
1361
        cache: false,
1362
        type: "GET",
1363
        //async: false,
1364
        dataType: "json",
1365
        timeout: TIMEOUT,
1366
        error: function(jqXHR, textStatus, errorThrown) {
1367
            try {
1368
                // close wizard and show error box
1369
                $("a#metadata-scrollable").data('overlay').close();
1370
                ajax_error(jqXHR.status, undefined, 'Get metadata', jqXHR.responseText);
1371
            } catch (err) {
1372
                ajax_error(-5, "UI Error", "Get metadata", err);
1373
            }
1374
        },
1375
        success: function(data, textStatus, jqXHR) {
1376
            // to list the new results in the edit dialog
1377
            if (keys_only) {
1378
                list_metadata_keys(serverID, data.metadata.values);
1379
            } else {
1380
                list_metadata(data);
1381
                list_metadata_keys(serverID, data.metadata.values);
1382
            }
1383
            //hide spinner
1384
            $('#metadata-wizard .large-spinner').hide();
1385
        }
1386
    });
1387
    return false;
1388
}
1389

    
1390
// delete metadata key-value pair
1391
function delete_metadata(serverID, meta_key) {
1392
    $.ajax({
1393
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1394
        type: "DELETE",
1395
        //async: false,
1396
        dataType: "json",
1397
        timeout: TIMEOUT,
1398
        error: function(jqXHR, textStatus, errorThrown) {
1399
            try {
1400
                // close wizard and show error box
1401
                $("a#metadata-scrollable").data('overlay').close();
1402
                ajax_error(jqXHR.status, undefined, 'Delete metadata', jqXHR.responseText);
1403
            } catch (err) {
1404
                ajax_error(-5, "UI Error", "Delete metadata", err);
1405
            }
1406
        },
1407
        success: function(data, textStatus, jqXHR) {
1408
                    // success: Do nothing, the UI is already updated
1409
        }
1410
    });
1411
    return false;
1412
}
1413

    
1414
// add metadata key-value pair
1415
function update_metadata(serverID, meta_key, meta_value) {
1416
    var payload = {
1417
        "meta": {
1418
        }
1419
    };
1420
    payload["meta"][meta_key] = meta_value;
1421

    
1422
    $.ajax({
1423
        url: API_URL + '/servers/' + serverID + '/meta/' + meta_key,
1424
        type: "PUT",
1425
        contentType: "application/json",
1426
        dataType: "json",
1427
        data: JSON.stringify(payload),
1428
        timeout: TIMEOUT,
1429
        error: function(jqXHR, textStatus, errorThrown) {
1430
            try {
1431
                // close wizard and show error box
1432
                $("a#metadata-scrollable").data('overlay').close();
1433
                ajax_error(jqXHR.status, undefined, 'add metadata', jqXHR.responseText);
1434
            } catch (err) {
1435
                ajax_error(-5, "UI Error", "add metadata", err);
1436
            }
1437
        },
1438
        success: function(data, textStatus, jqXHR) {
1439
            // success: Update icons if meta key is OS
1440
            if (meta_key == "OS") {
1441
                $("#metadata-wizard .machine-icon").attr("src","static/icons/machines/small/" + os_icon_from_value(meta_value) + '-' + $("#metadata-wizard div#on-off").text() + '.png');
1442
                var machine_icon = $("#machinesview-icon").find("div#" + serverID);
1443
                var machine_single = $("#machinesview-single").find("div#" + serverID);
1444

    
1445
                var os = os_icon_from_value(meta_value);
1446
                var state = $("#metadata-wizard div#on-off").text()
1447
                var state_single = $(".state", machine_single).hasClass("terminated-state") ? "off" : "on";
1448
                set_machine_os_image(machine_icon, "icon", state, os);
1449
                set_machine_os_image(machine_single, "single", state_single, os);
1450
            }
1451
        }
1452
    });
1453
    return false;
1454
}
1455

    
1456
// get stats
1457
function get_server_stats(serverID) {
1458
    $.ajax({
1459
        url: API_URL + '/servers/' + serverID + '/stats',
1460
        cache: false,
1461
        type: "GET",
1462
        //async: false,
1463
        dataType: "json",
1464
        timeout: TIMEOUT,
1465
        error: function(jqXHR, textStatus, errorThrown) {
1466
                // report error as text inline
1467
                $('#' + serverID + ' img.busy').hide();
1468
                $('#' + serverID + ' div.stat-error').show();
1469
        },
1470
        success: function(data, textStatus, jqXHR) {
1471
            // in icon view
1472
            if ( $.cookie('view') == 0 ) {
1473
                $('#' + serverID + ' img.busy').removeClass('busy');
1474
                $('#' + serverID + ' img.cpu').attr("src", data.stats.cpuBar);
1475
                $('#' + serverID + ' img.net').attr("src", data.stats.netBar);
1476
            }
1477
            // in single view
1478
            else if ( $.cookie('view') == 2 ) {
1479
                $('#' + serverID + ' div.cpu-graph img.stats').attr("src", data.stats.cpuTimeSeries);
1480
                $('#' + serverID + ' div.network-graph img.stats').attr("src", data.stats.netTimeSeries);
1481
            }
1482
        }
1483
    });
1484
    return false;
1485
}
1486

    
1487
// create network
1488
function create_network(networkName){
1489
    // ajax post start call
1490
    var payload = {
1491
        "network": { "name": networkName }
1492
    };
1493

    
1494
    $.ajax({
1495
        url: API_URL + '/networks',
1496
        type: "POST",
1497
        contentType: "application/json",
1498
        dataType: "json",
1499
        data: JSON.stringify(payload),
1500
        timeout: TIMEOUT,
1501
        error: function(jqXHR, textStatus, errorThrown) {
1502
            try {
1503
                // close wizard and show error box
1504
                $("a#networkscreate").overlay().close();
1505
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1506
            } catch (err) {
1507
                ajax_error(-5, "UI Error", "Create network", err);
1508
            }
1509
        },
1510
        success: function(data, textStatus, jqXHR) {
1511
            if ( jqXHR.status == '202') {
1512
                try {
1513
                    console.info('created network ' + networkName);
1514
                } catch(err) {}
1515
                /*
1516
                On success of this call nothing happens.
1517
                When the UI gets the first update containing the created server,
1518
                the creation wizard is closed and the new network is inserted
1519
                to the DOM. This is done in update_networks_view()
1520
                */
1521
            } else {
1522
                // close wizard and show error box
1523
                $("a#networkscreate").overlay().close();
1524
                ajax_error(jqXHR.status, undefined, 'Create network', jqXHR.responseText);
1525
            }
1526
        }
1527
    });
1528
    return false;
1529
}
1530

    
1531
// rename network
1532
function rename_network(networkID, networkName){
1533
    if (!networkID.length){
1534
        //ajax_success('DEFAULT');
1535
        return false;
1536
    }
1537
    // prepare payload
1538
    var payload = {
1539
        "network": {"name": networkName}
1540
    };
1541
    // ajax call
1542
    $.ajax({
1543
        url: API_URL + '/networks/' + networkID,
1544
        type: "PUT",
1545
        contentType: "application/json",
1546
        dataType: "json",
1547
        data: JSON.stringify(payload),
1548
        timeout: TIMEOUT,
1549
        error: function(jqXHR, textStatus, errorThrown) {
1550
            try {
1551
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1552
            } catch (err) {
1553
                ajax_error(-1, "UI Error", 'Rename network', err);
1554
            }
1555
        },
1556
        success: function(data, textStatus, jqXHR) {
1557
            if ( jqXHR.status == '204') {
1558
                try {
1559
                    console.info('renamed network' + networkID);
1560
                } catch(err) {}
1561
            } else {
1562
                ajax_error(jqXHR.status, undefined, 'Rename network', jqXHR.responseText);
1563
            }
1564
        }
1565
    });
1566
    return false;
1567
}
1568

    
1569
function delete_network(networkIDs){
1570
    if (!networkIDs.length){
1571
        //ajax_success('DEFAULT');
1572
        return false;
1573
    }
1574
    // get a network
1575
    var networkID = networkIDs.pop();
1576
    // ajax post destroy call can have an empty request body
1577
    var payload = {};
1578
    // ajax call
1579
    $.ajax({
1580
        url: API_URL + '/networks/' + networkID,
1581
        type: "DELETE",
1582
        contentType: "application/json",
1583
        dataType: "json",
1584
        data: JSON.stringify(payload),
1585
        timeout: TIMEOUT,
1586
        error: function(jqXHR, textStatus, errorThrown) {
1587
            try {
1588
                display_net_failure(jqXHR.status, networkID, 'Delete', jqXHR.responseText);
1589
            } catch (err) {
1590
                display_net_failure(0, networkID, 'Delete', jqXHR.responseText);
1591
            }
1592
        },
1593
        success: function(data, textStatus, jqXHR) {
1594
            if ( jqXHR.status == '204') {
1595
                try {
1596
                    console.info('deleted network ' + networkID);
1597
                } catch(err) {}
1598
                // continue with the rest of the servers
1599
                delete_network(networkIDs);
1600
            } else {
1601
                try {
1602
                    display_net_failure(jqXHR.status, networkID, 'Delete', jqXHR.responseText);
1603
                } catch (err) {
1604
                    display_net_failure(0, networkID, 'Delete', jqXHR.responseText);
1605
                }
1606
            }
1607
        }
1608
    });
1609
    return false;
1610
}
1611

    
1612
function add_server_to_network(networkID, serverIDs, serverNames, serverStates) {
1613
    if (!serverIDs.length){
1614
        // close the overlay when all the calls are made
1615
        $("a#add-machines-overlay").overlay().close();
1616
        return false;
1617
    }
1618
    // get a server
1619
    var serverID = serverIDs.pop();
1620
    var serverName = serverNames.pop();
1621
    var serverState = serverStates.pop();
1622
    // prepare payload
1623
    var payload = {
1624
            "add": { "serverRef": serverID }
1625
        };
1626
    // prepare ajax call
1627
    $.ajax({
1628
        url: API_URL + '/networks/' + networkID + '/action',
1629
        type: "POST",
1630
        contentType: "application/json",
1631
        dataType: "json",
1632
        data: JSON.stringify(payload),
1633
        timeout: TIMEOUT,
1634
        error: function(jqXHR, textStatus, errorThrown) {
1635
            try {
1636
                // close wizard and show error box
1637
                $("a#add-machines-overlay").data('overlay').close();
1638
                ajax_error(jqXHR.status, undefined, 'Add server to network', jqXHR.responseText);
1639
            } catch (err) {
1640
                ajax_error(-5, "UI Error", 'Add server to network', err);
1641
            }
1642
        },
1643
        success: function(data, textStatus, jqXHR) {
1644
            if ( jqXHR.status == '202') {
1645
                try {
1646
                    console.info('added server ' + serverID + ' to network ' + networkID);
1647
                } catch(err) {}
1648
                // toggle the reboot dialog
1649
                display_reboot_dialog(networkID, serverID, serverName, serverState);
1650
                // continue with the rest of the servers
1651
                add_server_to_network(networkID, serverIDs, serverNames, serverStates);
1652
            } else {
1653
                // close wizard and show error box
1654
                $("a#add-machines-overlay").data('overlay').close();
1655
                ajax_error(jqXHR.status, undefined, 'Add server to network', jqXHR.responseText);
1656
            }
1657
        }
1658
    });
1659
    return false;
1660
}
1661

    
1662
function remove_server_from_network(networkIDs, serverIDs, serverNames, serverStates) {
1663
    if (!networkIDs.length){
1664
        //ajax_success('DEFAULT');
1665
        return false;
1666
    }
1667
    // get a network and a server
1668
    var networkID = networkIDs.pop();
1669
    var serverID = serverIDs.pop();
1670
    var serverName = serverNames.pop();
1671
    var serverState = serverStates.pop();
1672
    // prepare payload
1673
    var payload = {
1674
            "remove": { "serverRef": serverID }
1675
        };
1676
    // prepare ajax call
1677
    $.ajax({
1678
        url: API_URL + '/networks/' + networkID + '/action',
1679
        type: "POST",
1680
        contentType: "application/json",
1681
        dataType: "json",
1682
        data: JSON.stringify(payload),
1683
        timeout: TIMEOUT,
1684
        error: function(jqXHR, textStatus, errorThrown) {
1685
            try {
1686
                ajax_error(jqXHR.status, undefined, 'Remove server form network', jqXHR.responseText);
1687
            } catch (err) {
1688
                ajax_error(-5, "UI Error", 'Remove server form network', err);
1689
            }
1690
        },
1691
        success: function(data, textStatus, jqXHR) {
1692
            if ( jqXHR.status == '202') {
1693
                try {
1694
                    console.info('deleted server ' + serverID + ' from network ' + networkID);
1695
                } catch(err) {}
1696
                // toggle the reboot dialog
1697
                display_reboot_dialog(networkID, serverID, serverName, serverState);
1698
                // continue with the rest of the servers
1699
                remove_server_form_network(networkIDs, serverIDs, serverNames, serverStates);
1700
            } else {
1701
                ajax_error(jqXHR.status, undefined, 'Remove server form network', jqXHR.responseText);
1702
            }
1703
        }
1704
    });
1705
    return false;
1706
}
1707

    
1708
function set_firewall(networkID, serverID, profile) {
1709
    if (!networkID.length || !serverID.length || !profile.length){
1710
        return false;
1711
    }
1712
    // prepare payload
1713
    var payload = {
1714
            "firewallProfile": { "profile": profile }
1715
        };
1716
    // prepare ajax call
1717
    $.ajax({
1718
        url: API_URL + '/servers/' + serverID + '/action',
1719
        type: "POST",
1720
        contentType: "application/json",
1721
        dataType: "json",
1722
        data: JSON.stringify(payload),
1723
        timeout: TIMEOUT,
1724
        error: function(jqXHR, textStatus, errorThrown) {
1725
            try {
1726
                ajax_error(jqXHR.status, undefined, 'Set firewall profile', jqXHR.responseText);
1727
            } catch (err) {
1728
                ajax_error(-5, "UI Error", 'Set firewall profile', err);
1729
            }
1730
        },
1731
        success: function(data, textStatus, jqXHR) {
1732
            if ( jqXHR.status == '202') {
1733
                try {
1734
                    console.info('for server ' + serverID + ' set firewall profile to ' + profile);
1735
                } catch(err) {}
1736
                //remove progress gif and toggle the content
1737
                $('div#net-' + networkID + '-server-' + serverID + ' button.firewall-apply').html(VARIOUS["APPLY"]);
1738
                $('div#net-' + networkID + '-server-' + serverID + ' button.firewall-apply').attr("disabled", false);
1739
                $('div#net-' + networkID + '-server-' + serverID + ' div.firewall-header').click();
1740
                // toggle the reboot dialog
1741
                var serverName = $('div#net-' + networkID + '-server-' + serverID + ' div.machine-name-div span.name').text();
1742
                var serverState = $('div#net-' + networkID + '-server-' + serverID + ' img.logo').attr('src').split('-')[1];
1743
                serverState = serverState.split('.')[0];
1744
                display_reboot_dialog(networkID, serverID, serverName, serverState);
1745
            } else {
1746
                ajax_error(jqXHR.status, undefined, 'Set firewall profile', jqXHR.responseText);
1747
            }
1748
        }
1749
    });
1750
    return false;
1751
}
1752

    
1753
// show the welcome screen
1754
function showWelcome() {
1755
    $("#view-select").fadeOut("fast");
1756
    $("#emptymachineslist").fadeIn("fast");
1757
    $("#machinesview").hide();
1758
}
1759

    
1760
// hide the welcome screen
1761
function hideWelcome() {
1762
    $("#emptymachineslist").fadeOut("fast");
1763
    $("#view-select").fadeIn("fast");
1764
    $("div#view-select").show();
1765
    $("#machinesview").show();
1766
}
1767

    
1768
function log_server_status_change(server_entry, new_status) {
1769
    // firebug console logging
1770
    try {
1771
        if ($("#machinesview-single").length > 0) {
1772
            console.info(server_entry.find("div.machine-details div.name").text() +
1773
                        ' from ' + server_entry.find(".state-label").text() +
1774
                        ' to ' + STATUSES[new_status]);
1775
        } else {
1776
            console.info(server_entry.find("div.name span.name").text() +
1777
                        ' from ' + server_entry.find(".status").text() +
1778
                        ' to ' + STATUSES[new_status]);
1779
        }
1780
    } catch(err) {}
1781
}
1782

    
1783
function get_flavor_params(flavorRef) {
1784
    var cpus, ram, disk;
1785
    if ( flavors.length > 0 ) {
1786
        var current_flavor = '';
1787
        for (i=0; i<flavors.length; i++) {
1788
            if (flavors[i]['id'] == flavorRef) {
1789
                current_flavor = flavors[i];
1790
            }
1791
        }
1792
        cpus = current_flavor['cpu'];
1793
        ram = current_flavor['ram'];
1794
        disk = current_flavor['disk'];
1795
    } else {
1796
        cpus = 'undefined';
1797
        ram = 'undefined';
1798
        disk = 'undefined';
1799
    }
1800
    return {'cpus': cpus, 'ram': ram, 'disk': disk};
1801
}
1802

    
1803
function get_image_params(imageRef) {
1804
    var image_name, image_size;
1805
    if ( images.length > 0 ) {
1806
        var current_image = '';
1807
        for (i=0; i<images.length; i++) {
1808
            if (images[i]['id'] == imageRef) {
1809
                current_image = images[i];
1810
            }
1811
        }
1812
        try {
1813
            image_name = current_image['name'];
1814
        } catch(err) { image_name = 'undefined'; }
1815
        try{
1816
            image_size = current_image['metadata']['values']['size'];
1817
        } catch(err) { image_size = 'undefined'; }
1818
    } else {
1819
        image_name = 'undefined';
1820
        image_size = 'undefined';
1821
    }
1822
    return {'name': image_name,'size': image_size};
1823
}
1824

    
1825
function get_public_ips(server) {
1826
    var ip4, ip6;
1827
    try {
1828
        if (server.addresses.values) {
1829
            $.each (server.addresses.values, function(i, value) {
1830
                if (value.id == 'public') {
1831
                    try {
1832
                        $.each (value.values, function(i, ip) {
1833
                            if (ip.version == '4') {
1834
                                ip4 = ip.addr;
1835
                            } else if (ip.version == '6') {
1836
                                ip6 = ip.addr;
1837
                            } else {
1838
                                ip4 = 'pending';
1839
                                ip6 = 'pending';
1840
                            }
1841
                        });
1842
                    } catch (err){
1843
                        try{console.info('Server ' + server.id + ' has invalid ips')}catch(err){};
1844
                        ip4 = 'pending';
1845
                        ip6 = 'pending';
1846
                    }
1847
                }
1848
            });
1849
        }
1850
    } catch (err) {
1851
        try{console.info('Server ' + server.id + ' has no network addresses')}catch(err){};
1852
        ip4 = 'pending';
1853
        ip6 = 'pending';
1854
    }
1855
    return {'ip4': ip4, 'ip6': ip6};
1856
}
1857

    
1858
function get_private_ips(server) {
1859

    
1860
}
1861

    
1862
function close_all_overlays() {
1863
        try {
1864
                $("a#networkscreate").overlay().close();
1865
        } catch(err) {}
1866
        try {
1867
                $('a#create').overlay().close();
1868
        } catch(err) {}
1869
        try {
1870
                $("a#add-machines-overlay").overlay().close();
1871
        } catch(err) {}
1872
        try {
1873
                $("a#metadata-scrollable").overlay().close();
1874
        } catch(err) {}
1875
        try {
1876
                $("a#msgbox").overlay().close();
1877
        } catch(err) {}
1878
        try {
1879
                $("a#feedbackbox").overlay().close();
1880
        } catch(err) {}
1881
}
1882

    
1883
// logout
1884
function user_session_logout() {
1885
    $.cookie("X-Auth-Token", null);
1886
    if (window.LOGOUT_REDIRECT !== undefined)
1887
    {
1888
        window.location = window.LOGOUT_REDIRECT;
1889
    } else {
1890
        window.location.reload();
1891
    }
1892
}
1893

    
1894
// action indicators
1895
function init_action_indicator_handlers(machines_view)
1896
{
1897
    // init once for each view
1898
    if (window.ACTION_ICON_HANDLERS == undefined)
1899
    {
1900
        window.ACTION_ICON_HANDLERS = {};
1901
    }
1902

    
1903
    if (machines_view in window.ACTION_ICON_HANDLERS)
1904
    {
1905
        return;
1906
    }
1907
    window.ACTION_ICON_HANDLERS[machines_view] = 1;
1908

    
1909
    if (machines_view == "list")
1910
    {
1911
        // totally different logic for list view
1912
        init_action_indicator_list_handlers();
1913
        return;
1914
    }
1915

    
1916
    function update_action_icon_indicators(force)
1917
    {
1918
        function show(el, action) {
1919
            $(".action-indicator", $(el)).attr("class", "action-indicator " + action);
1920
            $(".action-indicator", $(el)).show();
1921
        }
1922

    
1923
        function hide(el) {
1924
            $(".action-indicator", $(el)).hide();
1925
        }
1926

    
1927
        function get_pending_actions(el) {
1928
            return $(".confirm_single:visible", $(el));
1929
        }
1930

    
1931
        function other_indicators(el) {
1932
           return $("img.wave:visible, img.spinner:visible", $(el))
1933
        }
1934

    
1935
        $("div.machine:visible, div.single-container").each(function(index, el){
1936
            var el = $(el);
1937
            var pending = get_pending_actions(el);
1938
            var other = other_indicators(el);
1939
            var action = undefined;
1940
            var force_action = force;
1941
            var visible = $(el).css("display") == "block";
1942

    
1943
            if (force_action !==undefined && force_action.el !== el[0]) {
1944
                // force action for other vm
1945
                // skipping force action
1946
                force_action = undefined;
1947
            }
1948

    
1949
            if (force_action !==undefined && force_action.el === el[0]) {
1950
                action = force_action.action;
1951
            }
1952

    
1953
            if (other.length >= 1) {
1954
                return;
1955
            }
1956

    
1957
            if (pending.length >= 1 && force_action === undefined) {
1958
                action = $(pending.parent()).attr("class").replace("action-container","");
1959
            }
1960

    
1961
            if (action in {'console':''}) {
1962
                return;
1963
            }
1964

    
1965
            if (action !== undefined) {
1966
                show(el, action);
1967
            } else {
1968
                try {
1969
                    if (el.attr('id') == pending_actions[0][1])
1970
                    {
1971
                        return;
1972
                    }
1973
                } catch (err) {
1974
                }
1975
                hide(el);
1976
            }
1977

    
1978
        });
1979
    }
1980

    
1981
    // action indicators
1982
    $(".action-container").live('mouseover', function(evn) {
1983
        force_action = {'el': $(evn.currentTarget).parent().parent()[0], 'action':$(evn.currentTarget).attr("class").replace("action-container","")};
1984
        // single view case
1985
        if ($(force_action.el).attr("class") == "upper")
1986
        {
1987
            force_action.el = $(evn.currentTarget).parent().parent().parent()[0]
1988
        };
1989
        update_action_icon_indicators(force_action);
1990
    });
1991

    
1992
    $("img.spinner, img.wave").live('hide', function(){
1993
        update_action_icon_indicators();
1994
    });
1995
    // register events where icons should get updated
1996

    
1997
    // hide action indicator image on mouse out, spinner appear, wave appear
1998
    $(".action-container").live("mouseout", function(evn){
1999
        update_action_icon_indicators();
2000
    });
2001

    
2002
    $(".confirm_single").live("click", function(evn){
2003
        update_action_icon_indicators();
2004
    });
2005

    
2006
    $("img.spinner, img.wave").live('show', function(){
2007
        $("div.action-indicator").hide();
2008
    });
2009

    
2010
    $(".confirm_single button.no").live('click', function(evn){
2011
        $("div.action-indicator", $(evn.currentTarget).parent().parent()).hide();
2012
    });
2013

    
2014
    $(".confirm_multiple button.no").click(function(){
2015
        $("div.action-indicator").hide();
2016
    });
2017

    
2018
    $(".confirm_multiple button.yes").click(function(){
2019
        $("div.action-indicator").hide();
2020
    });
2021
}
2022

    
2023
function init_action_indicator_list_handlers()
2024
{
2025
    var skip_actions = { 'connect':'','details':'' };
2026

    
2027
    var has_pending_confirmation = function()
2028
    {
2029
        return $(".confirm_multiple:visible").length >= 1
2030
    }
2031

    
2032
    function update_action_indicator_icons(force_action, skip_pending)
2033
    {
2034
        // pending action based on the element class
2035
        var pending_action = $(".selected", $(".actions"))[0];
2036
        var selected = get_list_view_selected_machine_rows();
2037

    
2038
        // reset previous state
2039
        list_view_hide_action_indicators();
2040

    
2041
        if (pending_action == undefined && !force_action)
2042
        {
2043
            // no action selected
2044
            return;
2045
        }
2046

    
2047
        if (force_action != undefined)
2048
        {
2049
            // user forced action choice
2050
            var action_class = force_action;
2051
        } else {
2052
            // retrieve action name (reboot, stop, etc..)
2053
            var action_class = $(pending_action).attr("id").replace("action-","");
2054
        }
2055

    
2056
        selected.each(function(index, el) {
2057
            if (has_pending_confirmation() && skip_pending)
2058
            {
2059
                return;
2060
            }
2061
            var el = $(el);
2062
            var logo = $("img.list-logo", el);
2063
            $(".action-indicator", el).remove();
2064
            var cls = "action-indicator " + action_class;
2065
            // add icon div
2066
            logo.after('<div class="' + cls + '"></div>');
2067
            // hide os logo
2068
            $("img.list-logo", el).hide();
2069
        });
2070
    }
2071

    
2072
    // on mouseover we force the images to the hovered action
2073
    $(".actions a").live("mouseover", function(evn) {
2074
        var el = $(evn.currentTarget);
2075
        if (!el.hasClass("enabled"))
2076
        {
2077
            return;
2078
        }
2079
        var action_class = el.attr("id").replace("action-","");
2080
        if (action_class in skip_actions)
2081
        {
2082
            return;
2083
        }
2084
        update_action_indicator_icons(action_class, false);
2085
    });
2086

    
2087

    
2088
    // register events where icons should get updated
2089
    $(".actions a.enabled").live("click", function(evn) {
2090
        // clear previous selections
2091
        $("a.selected").removeClass("selected");
2092

    
2093
        var el = $(evn.currentTarget);
2094
        el.addClass("selected");
2095
        update_action_indicator_icons(undefined, false);
2096
    });
2097

    
2098
    $(".actions a").live("mouseout", function(evn) {
2099
        update_action_indicator_icons(undefined, false);
2100
    });
2101

    
2102
    $(".confirm_multiple button.no").click(function(){
2103
        list_view_hide_action_indicators();
2104
    });
2105

    
2106
    $(".confirm_multiple button.yes").click(function(){
2107
        list_view_hide_action_indicators();
2108
    });
2109

    
2110
    $("input[type=checkbox]").live('change', function(){
2111
        // pending_actions will become empty on every checkbox click/change
2112
        // line 154 machines_list.html
2113
        pending_actions = [];
2114
        if (pending_actions.length == 0)
2115
        {
2116
            $(".confirm_multiple").hide();
2117
            $("a.selected").each(function(index, el){$(el).removeClass("selected")});
2118
        }
2119
        update_action_indicator_icons(undefined, false);
2120
    });
2121

    
2122
}
2123

    
2124
function list_view_hide_action_indicators()
2125
{
2126
    $("tr td .action-indicator").remove();
2127
    $("tr td img.list-logo").show();
2128
}
2129

    
2130
function get_list_view_selected_machine_rows()
2131
{
2132
    var table = $("table.list-machines");
2133
    var rows = $("tr:has(input[type=checkbox]:checked)",table);
2134
    return rows;
2135
}
2136

    
2137
// machines images utils
2138
function set_machine_os_image(machine, machines_view, state, os, skip_reset_states, remove_state) {
2139
    var views_map = {'single': '.single-image', 'icon': '.logo'};
2140
    var states_map = {'on': 'state1', 'off': 'state3', 'hover': 'state4', 'click': 'state2'}
2141
    var sizes_map = {'single': 'large', 'icon': 'medium'}
2142

    
2143
    var size = sizes_map[machines_view];
2144
    var img_selector = views_map[machines_view];
2145
    var cls = states_map[state];
2146

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

    
2150
    var el = $(img_selector, machine);
2151
    var current_img = el.css("backgroundImage");
2152
    if (os == undefined){
2153
        new_img = current_img;
2154
    }
2155

    
2156
    // os changed
2157
    el.css("backgroundImage", new_img);
2158

    
2159
    // reset current state
2160
    if (skip_reset_states === undefined)
2161
    {
2162
        el.removeClass("single-image-state1");
2163
        el.removeClass("single-image-state2");
2164
        el.removeClass("single-image-state3");
2165
        el.removeClass("single-image-state4");
2166
    }
2167

    
2168
    if (remove_state !== undefined)
2169
    {
2170
        remove_state = "single-image-" + states_map[remove_state];
2171
        el.removeClass(remove_state);
2172
        return;
2173
    }
2174
    
2175
    // set proper state
2176
    el.addClass("single-image-" + cls);
2177
}
2178

    
2179

    
2180
// generic info box
2181
function show_feedback_form(msg, from_error) {
2182
    var box = $("#feedback-form");
2183
    box.addClass("notification-box");
2184

    
2185
    // initialize
2186
    box.find(".form-container").show();
2187
    box.find("textarea").val("");
2188
    box.find(".message").hide();
2189
    
2190
    var initial_msg = msg || undefined;
2191
    
2192
    var triggers = $("a#feedbackbox").overlay({
2193
        // some mask tweaks suitable for modal dialogs
2194
        mask: '#666',
2195
        top: '10px',
2196
        fixed: false,
2197
        closeOnClick: false,
2198
        oneInstance: false,
2199
        load: false
2200
    });
2201

    
2202
    
2203
    if (initial_msg && from_error) {
2204
        // feedback form from ajax_error window
2205
        box.find("textarea").val(initial_msg);
2206
        $("a#feedbackbox").overlay().onClose(function(){window.location.reload()});
2207
        box.find("textarea").height(200);
2208
        $("a#feedbackbox").overlay().onLoad(function(){box.find("textarea").focus().setCursorPosition(500);});
2209
        
2210
    }
2211

    
2212
    $("#feedback-form form").unbind("submit");
2213
    $("#feedback-form form").submit(function(event) {
2214
        event.preventDefault();
2215
            
2216
        // empty msg
2217
        if ($("textarea.feedback-text").val().replace(/^\s*|\s*$/,"") == "") {
2218
            alert($(".empty-error-msg", this).text());
2219
            return;
2220
        }
2221

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

    
2224
        $.ajax({
2225
            url: FEEDBACK_URL,
2226
            data: $(this).serialize(),
2227
            type: "POST",
2228
            // show loading
2229
            beforeSend: function() {box.find(".form-container").hide(); box.find(".sending").fadeIn() },
2230
            // hide form
2231
            complete: function() { box.find(".form-container").hide(); box.find(".sending").hide() },
2232
            // on success display success message
2233
            success: function() { box.find(".success").fadeIn(); box.find(".sending").hide() },
2234
            // display error message
2235
            error: function() { box.find(".errormsg").fadeIn(); box.find(".sending").hide() }
2236
        })
2237
    });
2238
    
2239
    $("a#feedbackbox").data('overlay').load();
2240

    
2241
    // reset feedback_pending for ajax_errors
2242
    window.FEEDBACK_PENDING = false;
2243
    return false;
2244
}
2245

    
2246
function get_user_data(extra_data) {
2247
    return $.extend({
2248
        'servers': $.extend({}, servers),
2249
        'client': {'browser': $.browser, 'screen': $.extend({}, screen), 'client': $.client},
2250
        'dates': {'now': new Date, 'lastUpdate': changes_since_date}
2251
    }, extra_data);
2252
}
2253

    
2254
function msg_box(config) {
2255
    var config = $.extend({'title':'Info message', 'content': 'this is an info message', 'ajax': false, 'extra':false}, config);
2256
    // prepare the error message
2257
    // bring up success notification
2258

    
2259
    var box = $("#notification-box");
2260
    box.addClass("notification-box");
2261
    box.addClass('success');
2262
    box.removeClass('error');
2263

    
2264
    var sel = function(s){return $(s, box)};
2265
    // reset texts
2266
    sel("h3 span.header-box").html("");
2267
    sel(".sub-text").html("");
2268
    sel(".password-container .password").html("");
2269
    sel("div.machine-now-building").html("");
2270
    
2271

    
2272
    // apply msg box contents
2273
    sel("h3 span.header-box").html(config.title);
2274
    sel("div.machine-now-building").html(config.content);
2275
    sel(".popup-header").removeClass("popup-header-error");
2276
    box.removeClass("popup-border-error");
2277
    sel(".popup-details").removeClass("popup-details-error");
2278
    sel(".popup-separator").removeClass("popup-separator-error");
2279
    
2280
    sel(".password-container").hide();
2281
    if (config.extra) {
2282
        sel(".password-container .password").html(config.extra);
2283
        sel(".password-container").show();
2284
    }
2285

    
2286
    var triggers = $("a#msgbox").overlay({
2287
        // some mask tweaks suitable for modal dialogs
2288
        mask: '#666',
2289
        top: '10px',
2290
        closeOnClick: false,
2291
        oneInstance: false,
2292
        load: false,
2293
        fixed: false,
2294
        onClose: function () {
2295
            // With partial refresh working properly,
2296
            // it is no longer necessary to refresh the whole page
2297
            // choose_view();
2298
        }
2299
    });
2300
    $("a#msgbox").data('overlay').load();
2301
    
2302
    var parse_data = config.parse_data || false;
2303
    var load_html = config.html || false;
2304
    var user_success = config.success || false;
2305
    config.ajax = config.ajax || {};
2306

    
2307
    // requested to show remote data in msg_box
2308
    if (config.ajax) {
2309
        $.ajax($.extend({ 
2310
            url:config.ajax, 
2311
            success: function(data){
2312
                // we want to get our data parsed before
2313
                // placing them in content
2314
                if (parse_data) {
2315
                    data = parse_data(data);
2316
                }
2317

    
2318
                // no json response
2319
                // load html body
2320
                if (load_html) {
2321
                    sel("div.machine-now-building").html(data);
2322
                } else {
2323

    
2324
                    if (data.title) {
2325
                        sel("h3 span.header-box").text(data.title);
2326
                    }
2327

    
2328
                    if (data.content) {
2329
                        sel("div.machine-now-building").html(data.content);
2330
                    }
2331
                    if (data.extra) {
2332
                        sel(".password-container .password").html(data.extra);
2333
                        sel(".password-container").show();
2334
                    }
2335
                    if (data.subinfo) {
2336
                        sel(".sub-text").html(data.subinfo);
2337
                    } else {
2338
                        sel(".sub-text").html("");
2339
                    }
2340
                }
2341

    
2342
                if (user_success) {
2343
                    user_success($("div.machine-now-building"));
2344
                }
2345
            },
2346
            error: function(xhr, status, err) {
2347
                ajax_error(-5, "UI Error", "Machine connect", err);
2348
            }
2349
        }, config.ajax_config));
2350
    }
2351
    return false;
2352
}
2353

    
2354

    
2355
function show_invitations() {
2356

    
2357
    handle_invitations = function(el) {
2358

    
2359
        // proper class to identify the overlay block
2360
        el.addClass("invitations");
2361

    
2362
        var cont = el;
2363
        var form = $(el).find("form");
2364

    
2365
        // remove garbage rows that stay in DOM between requests
2366
        $(".removable-field-row:hidden").remove();
2367

    
2368
        // avoid buggy behaviour, close all overlays if something went wrong
2369
        try {
2370
            // form is in content (form is not displayed if user has no invitations)
2371
            if ($("#invform #removable-name-container-1").length) {
2372
                $("#invform #removable-name-container-1").dynamicField();
2373
            }
2374
        } catch (err) {
2375
            close_all_overlays();
2376
        }
2377
        
2378
        // we copy/paste it on the title no need to show it twice
2379
        $(".invitations-left").hide();
2380

    
2381
        // reset title
2382
        $("#notification-box .header-box").html("");
2383
        $("#notification-box .header-box").html(window.INVITATIONS_TITLE + " " + $($(".invitations-left")[0]).text());
2384

    
2385
        // handle form submit
2386
        form.submit(function(evn){
2387
            evn.preventDefault();
2388

    
2389
            // do the post
2390
            $.post(form.attr("action"), form.serialize(), function(data) {
2391
                // replace data
2392
                $(cont).html(data); 
2393

    
2394
                // append all handlers again (new html data need to redo all changes)
2395
                handle_invitations(cont);
2396
            });
2397

    
2398
            return false;
2399
        });
2400
    }
2401
    
2402
    // first time clicked (show the msg box with /invitations content)
2403
    msg_box({title:window.INVITATIONS_TITLE, content:'', ajax:INVITATIONS_URL, html:true, success: function(el){ 
2404
        handle_invitations(el)}
2405
    });
2406
}
2407

    
2408

    
2409
function get_short_v6(v6, parts_to_keep) {
2410
    var parts = v6.split(":");
2411
    var new_parts = parts.slice(parts.length - parts_to_keep);
2412
    return new_parts.join(":");
2413
}
2414

    
2415
function fix_v6_addresses() {
2416

    
2417
    // what to prepend
2418
    var match = "...";
2419
    // long ip min length
2420
    var limit = 20;
2421
    // parts to show after the transformation
2422
    // (from the end)
2423
    var parts_to_keep_from_end = 4;
2424

    
2425
    $(".ipv6-text").each(function(index, el){
2426
        var el = $(el);
2427
        var ip = $(el).text();
2428
            
2429
        // transformation not applyied
2430
        // FIXME: use $.data for the condition
2431
        if (ip.indexOf(match) == -1 && ip != "pending") {
2432
            
2433
            // only too long ips
2434
            if (ip.length > 20) {
2435
                $(el).data("ipstring", ip);
2436
                $(el).text(match + get_short_v6(ip, parts_to_keep_from_end));
2437
                $(el).attr("title", ip);
2438
                $(el).tooltip({'tipClass':'tooltip ipv6-tip', 'position': 'center center'});
2439
            }
2440
        } else {
2441
            if (ip.indexOf(match) == 0) {
2442
            } else {
2443
                // not a long ip anymore
2444
                $(el).data("ipstring", undefined);
2445
                $(el).css({'text-decoration':'none'});
2446

    
2447
                if ($(el).data('tooltip')) {
2448
                    $(el).data('tooltip').show = function () {};
2449
                }
2450
            }
2451
        }
2452
    });
2453
}
2454

    
2455
function fix_server_name(str, limit, append) {
2456
    limit = limit || 30;
2457
    append = append || "...";
2458

    
2459
    if (str.length > limit) {
2460
        str = str.substring(0,limit-append.length) + append;
2461
    }
2462
    return str;
2463
}