root / ui / static / synnefo.js @ 8d08f18a
History | View | Annotate | Download (19.9 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 |
var error_timeout = 20000; |
40 |
var last_request = {};
|
41 |
|
42 |
|
43 |
Object.prototype.toString = function(o){ |
44 |
|
45 |
var parse = function(_o){ |
46 |
var a = [], t;
|
47 |
for(var p in _o){ |
48 |
if(_o.hasOwnProperty(p)){
|
49 |
t = _o[p]; |
50 |
if(t && typeof t == "object"){ |
51 |
a[a.length]= p + ":{ " + arguments.callee(t).join(", ") + "}"; |
52 |
} |
53 |
else {
|
54 |
if(typeof t == "string"){ |
55 |
a[a.length] = [ p+ ": \"" + t.toString() + "\"" ]; |
56 |
} |
57 |
else{
|
58 |
a[a.length] = [ p+ ": " + t.toString()];
|
59 |
} |
60 |
} |
61 |
} |
62 |
} |
63 |
return a;
|
64 |
|
65 |
} |
66 |
return "{" + parse(o).join(", ") + "}"; |
67 |
|
68 |
} |
69 |
|
70 |
// http://stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area
|
71 |
$.fn.setCursorPosition = function(pos) { |
72 |
if ($(this).get(0).setSelectionRange) { |
73 |
$(this).get(0).setSelectionRange(pos, pos); |
74 |
} else if ($(this).get(0).createTextRange) { |
75 |
var range = $(this).get(0).createTextRange(); |
76 |
range.collapse(true);
|
77 |
range.moveEnd('character', pos);
|
78 |
range.moveStart('character', pos);
|
79 |
range.select(); |
80 |
} |
81 |
} |
82 |
|
83 |
// indexOf prototype for IE
|
84 |
if (!Array.prototype.indexOf) {
|
85 |
Array.prototype.indexOf = function(elt /*, from*/) { |
86 |
var len = this.length; |
87 |
var from = Number(arguments[1]) || 0; |
88 |
from = (from < 0)
|
89 |
? Math.ceil(from) |
90 |
: Math.floor(from); |
91 |
if (from < 0) |
92 |
from += len; |
93 |
|
94 |
for (; from < len; from++) {
|
95 |
if (from in this && |
96 |
this[from] === elt)
|
97 |
return from;
|
98 |
} |
99 |
return -1; |
100 |
}; |
101 |
} |
102 |
|
103 |
// trim prototype for IE
|
104 |
if(typeof String.prototype.trim !== 'function') { |
105 |
String.prototype.trim = function() { |
106 |
return this.replace(/^\s+|\s+$/g, ''); |
107 |
} |
108 |
} |
109 |
|
110 |
// simple string format helper (http://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format)
|
111 |
String.prototype.format = function() { |
112 |
var formatted = this; |
113 |
for (var i = 0; i < arguments.length; i++) { |
114 |
var regexp = new RegExp('\\{'+i+'\\}', 'gi'); |
115 |
formatted = formatted.replace(regexp, arguments[i]);
|
116 |
} |
117 |
return formatted;
|
118 |
}; |
119 |
|
120 |
|
121 |
// Show VNC console
|
122 |
function vnc_attachment(host, port, password) { |
123 |
// FIXME: Must be made into parameters, in settings.py
|
124 |
//vnc = open("", "displayWindow",
|
125 |
// "status=yes,toolbar=yes,menubar=yes");
|
126 |
vd = document.open("application/x-vnc");
|
127 |
|
128 |
vd.writeln("[connection]");
|
129 |
vd.writeln("host=" + host);
|
130 |
vd.writeln("port=" + port);
|
131 |
vd.writeln("password=" + password);
|
132 |
|
133 |
vd.close(); |
134 |
} |
135 |
|
136 |
// connect to machine action
|
137 |
function machine_connect(serverIDs){ |
138 |
if (!serverIDs.length){
|
139 |
//ajax_success('DEFAULT');
|
140 |
return false; |
141 |
} |
142 |
|
143 |
// prefer metadata values for specific options (username, domain)
|
144 |
var username_meta_key = 'user'; |
145 |
var domain_meta_key = "domain"; |
146 |
|
147 |
var serverID = serverIDs.pop();
|
148 |
var machine = get_machine(serverID);
|
149 |
var serverName = machine.name;
|
150 |
|
151 |
try {
|
152 |
var serverIP = machine.addresses.values[0].values[0].addr; |
153 |
} catch (err) { var serverIP = 'undefined'; } |
154 |
|
155 |
try {
|
156 |
var os = os_icon(machine.metadata);
|
157 |
} catch (err) { var os = 'undefined'; } |
158 |
|
159 |
var username = ""; |
160 |
try {
|
161 |
username = machine.metadata.values[username_meta_key]; |
162 |
} catch (err) { username = undefined } |
163 |
|
164 |
var domain = ""; |
165 |
try {
|
166 |
domain = machine.metadata.values[domain_meta_key]; |
167 |
} catch (erro) { domain = undefined } |
168 |
|
169 |
var params_url = '?ip_address=' + serverIP + '&os=' + os + "&host_os=" + $.client.os + "&srv=" + serverID; |
170 |
|
171 |
if (username) {
|
172 |
params_url += "&username=" + username;
|
173 |
} |
174 |
|
175 |
if (domain) {
|
176 |
params_url += "&domain=" + domain;
|
177 |
} |
178 |
|
179 |
//if ($.client.os == "Windows" && os == "windows") {
|
180 |
//// request rdp file
|
181 |
//window.open('machines/connect' + params_url + "&rdp=1");
|
182 |
//return;
|
183 |
//}
|
184 |
|
185 |
// FIXME: I18n ???
|
186 |
var title = 'Connect to: ' + '<span class="machine-title"><img src="static/icons/machines/small/'+os+'-on.png" /> ' + fix_server_name(serverName) + '</span>'; |
187 |
|
188 |
// open msg box and fill it with json data retrieved from connect machine view
|
189 |
try {
|
190 |
// open msg box
|
191 |
msg_box({ |
192 |
title:title,
|
193 |
fixed: true, |
194 |
content:'loading...', |
195 |
extra:'', 'ajax':'machines/connect' + params_url, |
196 |
parse_data:function(data){ |
197 |
var box_content = "<a href='"+data.link.url+"'>"+data.link.title+"</a>"; |
198 |
if (!data.link.url) {
|
199 |
box_content = "<span class='cmd'>"+data.link.title+"</span>"; |
200 |
} |
201 |
data.title = false;
|
202 |
data.content = data.info; |
203 |
data.extra = box_content; |
204 |
return data;
|
205 |
} |
206 |
}); |
207 |
} catch (error) {
|
208 |
// if msg box fails fallback redirecting the user to the connect url
|
209 |
window.open('machines/connect' + params_url);
|
210 |
} |
211 |
|
212 |
|
213 |
// Restore os icon in list view
|
214 |
osIcon = $('#'+serverID).parent().parent().find('.list-logo'); |
215 |
osIcon.attr('src',osIcon.attr('os')); |
216 |
|
217 |
return false; |
218 |
} |
219 |
|
220 |
|
221 |
function msg_box(user_config) { |
222 |
var defaults = {'title':'Info message', 'content': 'this is an info message', 'ajax': false, 'extra':false}; |
223 |
var config = $.extend(defaults, user_config); |
224 |
|
225 |
// prepare the error message
|
226 |
// bring up success notification
|
227 |
var box = $("#notification-box"); |
228 |
box.addClass("notification-box");
|
229 |
box.addClass('success');
|
230 |
box.addClass(config.cls || '');
|
231 |
box.removeClass('error');
|
232 |
|
233 |
var sel = function(s){return $(s, box)}; |
234 |
// reset texts
|
235 |
sel("h3 span.header-box").html(""); |
236 |
sel(".sub-text").html(""); |
237 |
sel(".password-container .password").html(""); |
238 |
sel("div.machine-now-building").html(""); |
239 |
|
240 |
// apply msg box contents
|
241 |
sel("h3 span.header-box").html(config.title);
|
242 |
sel("div.machine-now-building").html(config.content);
|
243 |
sel(".sub-text").html(config.sub_content || ''); |
244 |
sel(".popup-header").removeClass("popup-header-error"); |
245 |
box.removeClass("popup-border-error");
|
246 |
sel(".popup-details").removeClass("popup-details-error"); |
247 |
sel(".popup-separator").removeClass("popup-separator-error"); |
248 |
|
249 |
sel(".password-container").hide();
|
250 |
if (config.extra) {
|
251 |
sel(".password-container .password").html(config.extra);
|
252 |
sel(".password-container").show();
|
253 |
} |
254 |
|
255 |
var conf = {
|
256 |
// some mask tweaks suitable for modal dialogs
|
257 |
mask: '#666', |
258 |
top: '10px', |
259 |
closeOnClick: false, |
260 |
oneInstance: false, |
261 |
load: false, |
262 |
onLoad: config.onLoad || false, |
263 |
fixed: config.fixed || false, |
264 |
onClose: function () { |
265 |
// With partial refresh working properly,
|
266 |
// it is no longer necessary to refresh the whole page
|
267 |
// choose_view();
|
268 |
} |
269 |
} |
270 |
|
271 |
var triggers = $("a#msgbox").overlay(conf); |
272 |
|
273 |
try {
|
274 |
conf = $("a#msgbox").data('overlay').getConf(); |
275 |
conf.fixed = config.fixed || false;
|
276 |
} catch (err) {}
|
277 |
$("a#msgbox").data('overlay').load(); |
278 |
|
279 |
var parse_data = config.parse_data || false; |
280 |
var load_html = config.html || false; |
281 |
var user_success = config.success || false; |
282 |
config.ajax = config.ajax || {}; |
283 |
|
284 |
// requested to show remote data in msg_box
|
285 |
if (config.ajax && !$.isEmptyObject(config.ajax)) { |
286 |
$.ajax($.extend({ |
287 |
url:config.ajax,
|
288 |
success: function(data){ |
289 |
// we want to get our data parsed before
|
290 |
// placing them in content
|
291 |
if (parse_data) {
|
292 |
data = parse_data(data); |
293 |
} |
294 |
|
295 |
// no json response
|
296 |
// load html body
|
297 |
if (load_html) {
|
298 |
sel("div.machine-now-building").html(data);
|
299 |
} else {
|
300 |
|
301 |
if (data.title) {
|
302 |
sel("h3 span.header-box").text(data.title);
|
303 |
} |
304 |
|
305 |
if (data.content) {
|
306 |
sel("div.machine-now-building").html(data.content);
|
307 |
} |
308 |
if (data.extra) {
|
309 |
sel(".password-container .password").html(data.extra);
|
310 |
sel(".password-container").show();
|
311 |
} |
312 |
if (data.subinfo) {
|
313 |
sel(".sub-text").html(data.subinfo);
|
314 |
} else {
|
315 |
sel(".sub-text").html(""); |
316 |
} |
317 |
} |
318 |
|
319 |
if (user_success) {
|
320 |
user_success($("div.machine-now-building")); |
321 |
} |
322 |
}, |
323 |
error: function(xhr, status, err) { |
324 |
ajax_error(-519, "UI Error", "Machine connect", err, this); |
325 |
} |
326 |
}, config.ajax_config)); |
327 |
} |
328 |
return false; |
329 |
} |
330 |
|
331 |
|
332 |
function show_api_overlay() { |
333 |
var config = {
|
334 |
title: window.API_OVERLAY_TITLE,
|
335 |
content: $(".api_overlay_content").html().replace("$api_key", $.cookie("X-Auth-Token")), |
336 |
extra: $.cookie("X-Auth-Token"), |
337 |
sub_content: window.API_OVERLAY_SUBCONTENT,
|
338 |
cls: "api_content", |
339 |
ajax: false |
340 |
} |
341 |
msg_box(config); |
342 |
} |
343 |
|
344 |
function show_invitations() { |
345 |
|
346 |
function display_resend_success(msg) { |
347 |
clear_resend_messages(); |
348 |
$("#invsent .message.success").text(msg).show(); |
349 |
} |
350 |
|
351 |
function display_resend_error(msg) { |
352 |
clear_resend_messages(); |
353 |
$("#invsent .message.errormsg").text(msg).show(); |
354 |
} |
355 |
|
356 |
// clear resent messages
|
357 |
function clear_resend_messages() { |
358 |
$("#invsent .message").hide(); |
359 |
} |
360 |
|
361 |
// register resent click handlers
|
362 |
function register_invitation_resends() { |
363 |
$(".invitations .resend-invitation").click(function() { |
364 |
var invid = $(this).attr("id"); |
365 |
|
366 |
if (invid == null) |
367 |
return;
|
368 |
|
369 |
var id = invid.split("-")[1]; |
370 |
|
371 |
if (id == null) |
372 |
return;
|
373 |
|
374 |
var child = $(this).find("img"); |
375 |
child.attr('src', '/static/progress-tiny.gif'); |
376 |
|
377 |
$.ajax({
|
378 |
type: "POST", |
379 |
url : "/invitations/resend", |
380 |
data : {invid : id}, |
381 |
success: function(msg) { |
382 |
display_resend_success("Invitation has been resent");
|
383 |
child.attr('src', '/static/resend.png'); |
384 |
}, |
385 |
error : function(xhr, status, error) { |
386 |
display_resend_error("Something seems to have gone wrong. " +
|
387 |
"Please try again in a few minutes.");
|
388 |
child.attr('src', '/static/resend.png'); |
389 |
} |
390 |
}); |
391 |
}); |
392 |
} |
393 |
|
394 |
handle_invitations = function(el) { |
395 |
|
396 |
// proper class to identify the overlay block
|
397 |
el.addClass("invitations");
|
398 |
|
399 |
var cont = el;
|
400 |
var form = $(el).find("form"); |
401 |
|
402 |
// remove garbage rows that stay in DOM between requests
|
403 |
$(".removable-field-row:hidden").remove(); |
404 |
|
405 |
// avoid buggy behaviour, close all overlays if something went wrong
|
406 |
try {
|
407 |
// form is in content (form is not displayed if user has no invitations)
|
408 |
if ($("#invform #removable-name-container-1").length) { |
409 |
$("#invform #removable-name-container-1").dynamicField(); |
410 |
} |
411 |
} catch (err) {
|
412 |
close_all_overlays(); |
413 |
} |
414 |
|
415 |
// we copy/paste it on the title no need to show it twice
|
416 |
$(".invitations-left").hide(); |
417 |
|
418 |
// sending finished or first invitations view
|
419 |
$(".invitations .sending").hide(); |
420 |
$(".invitations .submit").show(); |
421 |
$(".invitations #fieldheaders").show(); |
422 |
$(".invitations #fields").show(); |
423 |
|
424 |
// reset title
|
425 |
$("#notification-box .header-box").html(""); |
426 |
$("#notification-box .header-box").html(window.INVITATIONS_TITLE + " " + $($(".invitations-left")[0]).text()); |
427 |
|
428 |
// resend buttons
|
429 |
register_invitation_resends(); |
430 |
clear_resend_messages(); |
431 |
|
432 |
// handle form submit
|
433 |
form.submit(function(evn){
|
434 |
evn.preventDefault(); |
435 |
|
436 |
// sending...
|
437 |
$(".invitations .sending").show(); |
438 |
$(".invitations .submit").hide(); |
439 |
$(".invitations #fieldheaders").hide(); |
440 |
$(".invitations #fields").hide(); |
441 |
|
442 |
// do the post
|
443 |
$.post(form.attr("action"), form.serialize(), function(data) { |
444 |
// replace data
|
445 |
$(cont).html(data);
|
446 |
|
447 |
// append all handlers again (new html data need to redo all changes)
|
448 |
handle_invitations(cont); |
449 |
}); |
450 |
|
451 |
return false; |
452 |
}); |
453 |
} |
454 |
|
455 |
// first time clicked (show the msg box with /invitations content)
|
456 |
msg_box({ |
457 |
title:window.INVITATIONS_TITLE,
|
458 |
content:'', |
459 |
fixed: false, |
460 |
ajax:INVITATIONS_URL,
|
461 |
html:true, |
462 |
success: function(el){ |
463 |
handle_invitations(el) |
464 |
} |
465 |
}); |
466 |
} |
467 |
|
468 |
|
469 |
function get_short_v6(v6, parts_to_keep) { |
470 |
var parts = v6.split(":"); |
471 |
var new_parts = parts.slice(parts.length - parts_to_keep);
|
472 |
return new_parts.join(":"); |
473 |
} |
474 |
|
475 |
function fix_v6_addresses() { |
476 |
|
477 |
// what to prepend
|
478 |
var match = "..."; |
479 |
// long ip min length
|
480 |
var limit = 20; |
481 |
// parts to show after the transformation
|
482 |
// (from the end)
|
483 |
var parts_to_keep_from_end = 4; |
484 |
|
485 |
$(".machine .ipv6-text").each(function(index, el){ |
486 |
var el = $(el); |
487 |
var ip = $(el).text(); |
488 |
|
489 |
// transformation not applyied
|
490 |
// FIXME: use $.data for the condition
|
491 |
if (ip.indexOf(match) == -1 && ip != "pending") { |
492 |
|
493 |
// only too long ips
|
494 |
if (ip.length > 20) { |
495 |
$(el).data("ipstring", ip); |
496 |
$(el).text(match + get_short_v6(ip, parts_to_keep_from_end));
|
497 |
$(el).attr("title", ip); |
498 |
$(el).tooltip({'tipClass':'tooltip ipv6-tip', 'position': 'center center'}); |
499 |
} |
500 |
} else {
|
501 |
if (ip.indexOf(match) == 0) { |
502 |
} else {
|
503 |
// not a long ip anymore
|
504 |
$(el).data("ipstring", undefined); |
505 |
$(el).css({'text-decoration':'none'}); |
506 |
|
507 |
if ($(el).data('tooltip')) { |
508 |
$(el).data('tooltip').show = function () {}; |
509 |
} |
510 |
} |
511 |
} |
512 |
}); |
513 |
} |
514 |
|
515 |
// get stats
|
516 |
function get_server_stats(serverID) { |
517 |
|
518 |
// do not update stats if machine in build state
|
519 |
var vm = get_machine(serverID);
|
520 |
if (vm.status == "BUILD" && vm.stats_timeout) { |
521 |
els = get_current_view_stats_elements(vm.id); |
522 |
els.cpu.img.hide(); |
523 |
els.net.img.hide(); |
524 |
|
525 |
els.cpu.busy.show(); |
526 |
els.net.busy.show(); |
527 |
return;
|
528 |
} |
529 |
|
530 |
$.ajax({
|
531 |
repeated: true, |
532 |
url: API_URL + '/servers/' + serverID + '/stats', |
533 |
cache: false, |
534 |
type: "GET", |
535 |
//async: false,
|
536 |
dataType: "json", |
537 |
timeout: TIMEOUT,
|
538 |
error: function(jqXHR, textStatus, errorThrown) { |
539 |
handle_api_error(-21, undefined, 'Get server stats', jqXHR, textStatus, errorThrown, this); |
540 |
}, |
541 |
success: function(data, textStatus, jqXHR) { |
542 |
update_machine_stats(serverID, data); |
543 |
}, |
544 |
|
545 |
// pass server id to ajax settings
|
546 |
serverID: serverID
|
547 |
}); |
548 |
return false; |
549 |
} |
550 |
|
551 |
function get_progress_details(id) { |
552 |
var vm = get_machine(id);
|
553 |
var progress = vm.progress;
|
554 |
|
555 |
// no details for active machines
|
556 |
if (!vm.status == "BUILD") { |
557 |
return false; |
558 |
} |
559 |
|
560 |
// check if images not loaded yet
|
561 |
try {
|
562 |
var image = synnefo.storage.images.get(vm.imageRef).attributes;
|
563 |
var size = image.size;
|
564 |
} catch (err) {
|
565 |
// images not loaded yet (can this really happen ??)
|
566 |
return;
|
567 |
} |
568 |
|
569 |
var to_copy = size;
|
570 |
var copied = (size * progress / 100).toFixed(2); |
571 |
var status = "INIT" |
572 |
|
573 |
// apply state
|
574 |
if (progress > 0) { status = "IMAGE_COPY" } |
575 |
if (progress >= 100) { status = "FINISH" } |
576 |
|
577 |
// user information
|
578 |
var msg = BUILDING_MESSAGES[status];
|
579 |
|
580 |
// image copy state display extended user information
|
581 |
//if (status == "IMAGE_COPY") {
|
582 |
//msg = msg.format(readablizeBytes(copied*(1024*1024)), readablizeBytes(to_copy*(1024*1024)), progress)
|
583 |
//}
|
584 |
|
585 |
var progress_data = {
|
586 |
'percent': vm.progress,
|
587 |
'build_status': status,
|
588 |
'copied': copied,
|
589 |
'to_copy': size,
|
590 |
'msg': msg
|
591 |
} |
592 |
|
593 |
return progress_data;
|
594 |
} |
595 |
|
596 |
// display user friendly bytes amount
|
597 |
function readablizeBytes(bytes) { |
598 |
var s = ['bytes', 'kb', 'MB', 'GB', 'TB', 'PB']; |
599 |
var e = Math.floor(Math.log(bytes)/Math.log(1024)); |
600 |
return (bytes/Math.pow(1024, Math.floor(e))).toFixed(2)+" "+s[e]; |
601 |
} |
602 |
|
603 |
// machines images utils
|
604 |
function set_machine_os_image(machine, machines_view, state, os, skip_reset_states, remove_state) { |
605 |
var views_map = {'single': '.single-image', 'icon': '.logo'}; |
606 |
var states_map = {'on': 'state1', 'off': 'state3', 'hover': 'state4', 'click': 'state2'} |
607 |
var sizes_map = {'single': 'large', 'icon': 'medium'} |
608 |
|
609 |
var size = sizes_map[machines_view];
|
610 |
var img_selector = views_map[machines_view];
|
611 |
var cls = states_map[state];
|
612 |
|
613 |
if (os === "unknown") { os = "okeanos" } ; |
614 |
var new_img = 'url("./static/icons/machines/' + size + '/' + os + '-sprite.png")'; |
615 |
|
616 |
var el = $(img_selector, machine); |
617 |
var current_img = el.css("backgroundImage"); |
618 |
if (os == undefined){ |
619 |
new_img = current_img; |
620 |
} |
621 |
|
622 |
// os changed
|
623 |
el.css("backgroundImage", new_img);
|
624 |
|
625 |
// reset current state
|
626 |
if (skip_reset_states === undefined) |
627 |
{ |
628 |
el.removeClass("single-image-state1");
|
629 |
el.removeClass("single-image-state2");
|
630 |
el.removeClass("single-image-state3");
|
631 |
el.removeClass("single-image-state4");
|
632 |
} |
633 |
|
634 |
if (remove_state !== undefined) |
635 |
{ |
636 |
remove_state = "single-image-" + states_map[remove_state];
|
637 |
el.removeClass(remove_state); |
638 |
return;
|
639 |
} |
640 |
|
641 |
// set proper state
|
642 |
el.addClass("single-image-" + cls);
|
643 |
} |
644 |
|
645 |
// return machine entry from serverID
|
646 |
function get_machine(serverID) { |
647 |
return synnefo.storage.vms.get(serverID).attributes;
|
648 |
} |
649 |
|