/**
*
* @fileOverview Chooser (vm list) singleton. Provides vboxChooser
* @author Ian Moore (imoore76 at yahoo dot com)
* @version $Id: chooser.js 591 2015-04-11 22:40:47Z imoore76 $
* @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com)
*
*/
/**
* Chooser selection mode constants
*/
var vboxSelectionModeNone = 0;
var vboxSelectionModeSingleVM = 1;
var vboxSelectionModeMultiVM = 2;
var vboxSelectionModeSingleGroup = 3;
/**
* @namespace vboxChooser
*
* Draws machine selection chooser and controls selection list
* @see js/eventlistener.js
*/
var vboxChooser = {
// VM list
vms : {},
// VM tool tip
_vmToolTip : '%1 %2 since %3 Session %4',
// Anchor element
_anchorid : null,
_anchor : null,
/* Internal list of all unique selected items */
_selectedList : [],
/* List of unique selected VMs */
selectedVMs : [],
/* Holds group definitions */
_groupDefs : [],
/* selection mode can be
var vboxSelectionModeNone = 0,
var vboxSelectionModeSingleVM = 1,
var vboxSelectionModeMultiVM = 2,
var vboxSelectionModeSingleGroup = 3,
*/
selectionMode : vboxSelectionModeNone,
/* Check phpVirtualBox version and VirtualBox
* version compatibility.
*/
_versionChecked : false,
/* Some items are not editable while vmGroup
* definitions are being written
*/
_editable : true,
/* Context menus */
_vmContextMenuObj : null,
_vmGroupContextMenuObj : null,
/* Holds history of showing only single groups */
_showOnlyGroupHistory : [],
/* Group definition extra value key */
_groupDefinitionKey : '',
/* Whether chooser is in compact mode or not */
_compact : false,
/**
* Set anchor id to draw to
*/
setAnchorId : function(aid) {
vboxChooser._anchorid = aid;
vboxChooser._anchor = $('#'+aid);
vboxChooser._anchor.html("
");
vboxChooser._anchor.hover(function(){
$(this).addClass('vboxChooserDropTargetHoverRoot');
},function() {
$(this).removeClass('vboxChooserDropTargetHoverRoot');
});
$(window).resize(function(){
// Get anchor id and add / remove class
var w = parseInt($(vboxChooser._anchor).innerWidth());
if(w < 120) {
$(vboxChooser._anchor).addClass('vboxChooserMini');
vboxChooser._compact = true;
} else {
$(vboxChooser._anchor).removeClass('vboxChooserMini');
vboxChooser._compact = false;
}
vboxChooser._resizeElements(true);
});
},
/**
* Set context menus
*
*/
setContextMenu : function(target, menuitems) {
switch(target) {
// Group menu
case 'group':
vboxChooser._vmGroupContextMenuObj = new vboxMenu({'name': vboxChooser._anchorid+'vmgroups',
'menuItems': menuitems,
'language_context': 'UIActionPool'});
vboxChooser._vmGroupContextMenuObj.update();
break;
// VM Menu
case 'vm':
vboxChooser._vmContextMenuObj = new vboxMenu({'name': vboxChooser._anchorid+'vms',
'menuItems': menuitems,
'language_context': 'UIActionPool'});
vboxChooser._vmContextMenuObj.update();
break;
// Main list menu
case 'anchor':
var vboxChooserPaneMenu = new vboxMenu({'name': vboxChooser._anchorid+'Pane',
'menuItems': menuitems,
'language_context': 'UIActionPool'});
$('#'+vboxChooser._anchorid).parent().contextMenu({
menu: vboxChooserPaneMenu.menuId()
},
vboxChooserPaneMenu.menuClickCallback
);
break;
default:
vboxAlert('vboxChooser::setContextMenu: unknown context menu type (' + target + ')');
}
},
/*
* Return true if a selected VM is in the given state
*/
isSelectedInState : function(state) {
for(var i = 0; i < vboxChooser.selectedVMs.length; i++) {
if(vboxVMStates['is'+state](vboxVMDataMediator.getVMData(vboxChooser.selectedVMs[i])))
return true;
}
return false;
},
/**
* Return true if the passed VM is selected
*/
isVMSelected : function(vmid) {
return (jQuery.inArray(vmid,vboxChooser.selectedVMs) > -1);
},
/**
* Return selected VM data in array
*/
getSelectedVMsData : function() {
var vms = [];
for(var i = 0; i < vboxChooser.selectedVMs.length; i++) {
vms[vms.length] = vboxVMDataMediator.getVMData(vboxChooser.selectedVMs[i]);
}
return vms;
},
/**
* Triggered when selection list has changed
*/
selectionListChanged : function(selectionList) {
if(!selectionList) selectionList = [];
selectionMode = vboxSelectionModeNone;
// Hold unique selected VMs
var vmListUnique = {};
for(var i = 0; i < selectionList.length; i++) {
if(selectionList[i].type == 'group') {
vboxChooser.getGroupElement(selectionList[i].groupPath, true).find('table.vboxChooserVM:not(.ui-draggable-dragging)').each(function(idx,elm){
if(elm) {
var vmid = $(elm).data('vmid');
if(vmid)
vmListUnique[vmid] = vmid;
}
});
switch(selectionMode) {
case vboxSelectionModeSingleGroup:
case vboxSelectionModeSingleVM:
selectionMode = vboxSelectionModeMultiVM;
break;
case vboxSelectionModeNone:
selectionMode = vboxSelectionModeSingleGroup;
}
} else {
switch(selectionMode) {
case vboxSelectionModeNone:
selectionMode = vboxSelectionModeSingleVM;
break;
default:
selectionMode = vboxSelectionModeMultiVM;
}
vmListUnique[selectionList[i].id] = selectionList[i].id;
}
}
// Change selection list
var selectedVMs = [];
for(var i in vmListUnique) {
selectedVMs[selectedVMs.length] = i;
}
vboxChooser.selectedVMs = selectedVMs;
// If there is only one unique vm selected,
// selection mode becomes single VM if the
// current selection mode is not singleGroup
if(vboxChooser.selectedVMs.length == 1 && selectionMode != vboxSelectionModeSingleGroup)
selectionMode = vboxSelectionModeSingleVM;
vboxChooser.selectionMode = selectionMode;
vboxChooser._selectedList = selectionList;
$('#vboxPane').trigger('vmSelectionListChanged',[vboxChooser]);
},
/**
* Return the single selected VM's id if
* only one vm is selected. Else null.
*/
getSingleSelectedId : function() {
if(vboxChooser.selectedVMs.length == 1) {
return vboxChooser.selectedVMs[0];
}
return null;
},
/*
* Return a single vm if only one is selected.
* Else null.
*/
getSingleSelected : function() {
if(vboxChooser.selectedVMs.length == 1) {
return vboxVMDataMediator.getVMData(vboxChooser.selectedVMs[0]);
}
return null;
},
/*
* Update list of VMs from data received
* from ajax query
*/
updateList : function(vmlist) {
// We were stopped before the request returned data
if(!vboxChooser._running) return;
// No list? Something is wrong
if(!vmlist) {
phpVirtualBoxFailure();
vboxChooser.stop();
vboxChooser._anchor.children().remove();
return;
}
// Remove spinner
vboxChooser._anchor.children().remove();
// Render host
vboxChooser._anchor.append(vboxChooser.vmHTML(
{
'id':'host',
'state':'Hosting',
'owner':'',
'name':$('#vboxPane').data('vboxConfig').name,
'OSTypeId':'VirtualBox_Host'
}
));
// Render root group
vboxChooser._anchor.append(vboxChooser.groupHTML("/"));
// Enforce VM ownership
if($('#vboxPane').data('vboxConfig').enforceVMOwnership && !$('#vboxPane').data('vboxSession').admin) {
vmlist = jQuery.grep(vmlist,function(vm,i){
return (vm.owner == $('#vboxPane').data('vboxSession').user);
});
}
var groups = [];
// Each item in list
for(var i = 0; i < vmlist.length; i++) {
// Update
vboxChooser.updateVMElement(vmlist[i], true);
groups = groups.concat(vmlist[i].groups);
}
// Sort groups
var groupsSorted = {};
for(var i = 0; i < groups.length; i++) {
if(groupsSorted[groups[i]]) continue;
groupsSorted[groups[i]] = true;
var gElm = vboxChooser.getGroupElement(groups[i], true);
if(gElm[0]) vboxChooser.sortGroup(gElm);
}
// compose group definitions
vboxChooser.composeGroupDef();
// Set initial resize
vboxChooser._initialResize = true;
vboxChooser._resizeElements(true);
},
/*
* Save collapsed group list
*/
_collapsedGroups : [],
_saveCollapsedGroups : function(){
// Write out collapsed group list
var cGroupList = [];
vboxChooser._anchor.find('div.vboxVMGroupCollapsed:not(.ui-draggable-dragging)').each(function(idx,elm) {
cGroupList[cGroupList.length] = $(elm).data('vmGroupPath');
});
var groupListKey = $('#vboxPane').data('vboxConfig').key+'-collapsedGroups';
vboxSetLocalDataItem(groupListKey, cGroupList.join(','), true);
// Cache instead of using local storage
vboxChooser._collapsedGroups = cGroupList;
},
/*
* Return true if group is collapsed
*/
_isGroupCollapsed : function(gpath) {
return(jQuery.inArray(gpath,vboxChooser._collapsedGroups) > -1);
},
/*
* Resize group and VM titles
*/
_scrollbarWidth : 0,
_scrollbarWasVisible: false,
_initialResize: false,
_resizeElements : function(forceResize) {
// Haven't completed our initial resizing yet
if(!vboxChooser._initialResize) {
return;
}
var sbVisible = (vboxChooser._anchor.get(0).scrollHeight > vboxChooser._anchor.height());
// Nothing changed since resize
if(!forceResize && (sbVisible == vboxChooser._scrollbarWasVisible)) {
return;
}
vboxChooser._scrollbarWasVisible = sbVisible;
var groupTitleWidth = vboxChooser._anchor.width() - (vboxChooser._compact ? 22 : 32) - (sbVisible ? vboxChooser._scrollbarWidth : 0);
var vmTitleWidth = groupTitleWidth - (vboxChooser._compact ? -12 : 18); // (2px padding on .vboxChooserGroupVMs +
// 2px border on table + 4px margin on icon) * 2
var groupLevelOffset = (vboxChooser._compact ? 8 : 8); // (2px margin + 2px border) * 2
// Now that we have sizes, we can inject styles
$('#vboxChooserStyle').empty().remove();
var styleRules = [];
var path = ['div.vboxChooserGroupRootLevel'];
// Special case for root level VM list
styleRules[styleRules.length] = 'div.vboxChooserGroupRootLevel > div.vboxChooserGroupVMs table.vboxChooserVM div.vboxFitToContainer { width: ' + vmTitleWidth + 'px; }';
// Special case for group header when only showing one group
styleRules[styleRules.length] = 'div.vboxChooserGroupShowOnly.vboxChooserGroupRootLevel > div.vboxChooserGroupHeader span.vboxChooserGroupName { max-width: ' + (groupTitleWidth - 4) + 'px; }';
// Bottom group resize bars
styleRules[styleRules.length] = 'div.vboxChooserGroupRootLevel > div.vboxChooserDropTargetBottom { width: ' + (groupTitleWidth) + 30 + 'px; }';
for(var i = 1; i < 11; i++) {
// Group titles at this level
styleRules[styleRules.length] = path.join(' > ') + ' > div.vboxChooserGroup > div.vboxChooserGroupHeader span.vboxChooserGroupName { max-width: ' + (groupTitleWidth - (i*groupLevelOffset)) + 'px; }';
// VM titles at this level
styleRules[styleRules.length] = path.join(' > ') + ' > div.vboxChooserGroup > div.vboxChooserGroupVMs table.vboxChooserVM div.vboxFitToContainer { width: ' + (vmTitleWidth - (i*(groupLevelOffset))) + 'px; }';
// Bottom group resize bars
styleRules[styleRules.length] = path.join(' > ') +' > div.vboxChooserGroup > div.vboxChooserDropTargetBottom { width: ' + (groupTitleWidth + 30 - (i*groupLevelOffset)) + 'px; }';
path[path.length] = 'div.vboxChooserGroup';
}
// Style for minified vmlist
if(vboxChooser._compact) {
// Title moves left
styleRules[styleRules.length] = 'div.vboxChooserGroup > div.vboxChooserGroupVMs table.vboxChooserVM div.vboxVMName { position: relative; left: -20px; }';
// Icon moves down
styleRules[styleRules.length] = 'div.vboxChooserGroup > div.vboxChooserGroupVMs table.vboxChooserVM img.vboxVMIcon { position: relative; top: 8px; }';
// State text goes away
styleRules[styleRules.length] = 'div.vboxChooserGroup > div.vboxChooserGroupVMs table.vboxChooserVM span.vboxVMState { display: none; }';
// Less padding
styleRules[styleRules.length] = 'div.vboxChooserGroup > div.vboxChooserGroupVMs table.vboxChooserVM td { padding: 0px; }';
// Some group header items and drop targets go away
styleRules[styleRules.length] = 'div.vboxChooserGroup > div.vboxChooserGroupHeader > .vboxChooserGroupNameArrowCollapse, #' +vboxChooser._anchorid + ' div.vboxChooserGroup .vboxChooserDropTarget { display: none; }';
styleRules[styleRules.length] = 'div.vboxChooserGroup { overflow: hidden; }';
// host
styleRules[styleRules.length] = '#vboxChooserVMHost .vboxVMState { display: none; }';
// group header
styleRules[styleRules.length] = 'div.vboxChooserGroup div.vboxChooserGroupHeader { height: auto; padding: 2px; }';
}
$('head').append('');
},
/*
* Get group element by path
*/
getGroupElement : function(gpath, noCreate) {
if(!gpath) gpath = '/';
var gnames = gpath.split('/');
var groot = vboxChooser._anchor.children('div.vboxChooserGroup:not(.ui-draggable-dragging)');
for(var i = 1; i < gnames.length; i++) {
if(!gnames[i]) continue;
var group = groot.children('div.vboxChooserGroup:not(.ui-draggable-dragging)').children('div.vboxChooserGroupIdentifier[title="'+gnames[i]+'"]').parent();
// If it does not exist, create it
if(!group[0]) {
if(noCreate) return null;
var gpath = '/';
for(var a = 1; a <= i; a++) {
gpath = gpath + '/' + gnames[a];
}
gpath = gpath.replace('//','/');
vboxChooser.groupHTML(gpath).insertBefore(groot.children('div.vboxChooserGroupVMs'));
vboxChooser.sortGroup(groot);
// Resize chooser elements
vboxChooser._initialResize = true;
vboxChooser._resizeElements();
groot = groot.children('div.vboxChooserGroup:not(.ui-draggable-dragging)').children('div.vboxChooserGroupIdentifier[title="'+gnames[i]+'"]').parent();
} else {
groot = group;
}
}
return groot;
},
/*
*
* Update VM elements
*
*/
updateVMElement : function(vmUpdate, newVM) {
// Not running.. don't do anything
if(!vboxChooser._running) return;
// Stale event after vm was removed
if(!vmUpdate) return;
// New VM
if(newVM) {
// New VM.. add it to groups..
if(!vmUpdate.groups || vmUpdate.groups.length == 0)
vmUpdate.groups = ['/'];
for(var i = 0; i < vmUpdate.groups.length; i++) {
var gElm = $(vboxChooser.getGroupElement(vmUpdate.groups[i]));
vboxChooser.vmHTML(vmUpdate).appendTo(
gElm.children('div.vboxChooserGroupVMs')
);
}
// Existing VM. Replace existing elements
} else {
$('#'+vboxChooser._anchorid).find('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmUpdate.id).each(function(i,elm){
var newHTML = vboxChooser.vmHTML(vmUpdate);
if($(elm).hasClass('vboxListItemSelected')) {
$(newHTML).addClass('vboxListItemSelected').removeClass('vboxHover');
}
$(elm).children().replaceWith(newHTML.children());
});
}
},
/*
* Returns true if there are VMs with ID vmid that are not selected
*/
vmHasUnselectedCopy : function (vmid) {
return ($(vboxChooser._anchor).find('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmid+':not(.vboxListItemSelected)').length > 0);
},
/*
* Remove selected VMs from the list and rewrite group definitions
* this assumes that there are other copies of these VMs that are not
* selected.
*/
removeVMs : function(vmids) {
for(var i = 0; i < vmids.length; i++) {
$(vboxChooser._anchor).find('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmids[i]+'.vboxListItemSelected').remove();
}
// Update selection list
vboxChooser._selectedList = vboxChooser._selectedList.filter(function(v){
return (v.type == 'group' || (jQuery.inArray(v.id, vmids) == -1));
});
// Tell interface that selection list has changed
vboxChooser.selectionListChanged(vboxChooser._selectedList);
// compose and save group definitions
vboxChooser.composeGroupDef(true);
// Possible resize needed
vboxChooser._resizeElements(true);
},
/*
* Generate HTML from VM definition
*/
vmHTML : function (vmn) {
var tbl = $('
').attr({'rowspan':'2'}).html("").appendTo(tr);
}
// VM Name
var td = $('
').attr({'class':'vboxVMTitle'});
// Host will have HTML in name and unique id
if(vmn.id == 'host') {
$(tbl).attr('id', 'vboxChooserVMHost');
// Check for multiple server config
if($('#vboxPane').data('vboxConfig').servers.length) {
// If there are multiple servers configured, setup menu
if(!$('#vboxServerMenu')[0]) {
var servers = $('#vboxPane').data('vboxConfig').servers;
var ul = $('
').attr({'id':'vboxServerMenu','style':'display: none','class':'contextMenu'});
for(var i = 0; i < servers.length; i++) {
$('').html("').html(servers[i].name).text() + "' style='background-image: url(images/vbox/OSE/VirtualBox_16px.png);'>"+$('').html(servers[i].name).text()+"").appendTo(ul);
}
$('#vboxPane').append(ul);
}
var span = $('').attr({'class':'vboxServerLink'}).text('('+$('#vboxPane').data('vboxConfig').name+')').contextMenu({
menu: 'vboxServerMenu',
button: 0,
mode: 'menu'
},
function(a) {
if(a == $('#vboxPane').data('vboxConfig').name) return;
// Show loading screen
var l = new vboxLoader();
l.showLoading();
// Empty selection list
vboxChooser.selectionListChanged();
// Unsubscribe from events
$.when(vboxEventListener.stop()).done(function() {
// Expire data mediator data
vboxVMDataMediator.expireAll();
// Trigger host change
vboxSetCookie("vboxServer",a);
$('#vboxPane').trigger('hostChange',[a]);
}).always(function(){
// remove loading screen
l.removeLoading();
});
}
);
$(td).html('VirtualBox ').append(span);
} else {
$(td).html('VirtualBox ('+vmn.name+')');
}
// Not rendering host
} else {
$(td).append('
');
// Check for version mismatches?
if(!vboxChooser._versionChecked) {
vboxChooser._versionChecked = true;
var vStr = $('#vboxPane').data('vboxConfig').phpvboxver.substring(0,$('#vboxPane').data('vboxConfig').phpvboxver.indexOf('-'));
var vers = $('#vboxPane').data('vboxConfig').version.string.replace('_OSE','').split('.');
if(vers[0]+'.'+vers[1] != vStr) {
vboxAlert('This version of phpVirtualBox ('+$('#vboxPane').data('vboxConfig').phpvboxver+') is incompatible with VirtualBox ' + $('#vboxPane').data('vboxConfig').version.string + ". You probably need to download the latest phpVirtualBox " + vers[0]+'.'+vers[1] + "-x.
See the Versioning section below the file list in the link for more information