/** * * @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({'class':'vboxChooserItem-'+vboxChooser._anchorid+'-'+vmn.id + " vboxChooserVM"}) .on('mousedown',vboxChooser.selectItem) .hoverClass('vboxHover').data('vmid',vmn.id); // Drag-and-drop functionality ///////////////////////////////// if(vmn.id != 'host' && $('#vboxPane').data('vboxSession').admin) { $(tbl).draggable({'cursorAt':{left: -10, top: -10},'helper':function(){ return $(this).clone().css({'width':($(this).width()+2)+'px','display':'inline','background':'#fff','border-color':'#69f'}).removeClass('vboxHover'); // drag start },'start':function(e) { if(!vboxChooser._editable) return false; $(vboxChooser._anchor).disableSelection(); vboxChooser._dragging = vmn.id; $(vboxChooser._anchor).find('table.vboxHover').removeClass('vboxHover'); // drag stop },'stop':function(e) { vboxChooser.vmDropped(e, $(this)); }}); } // Functionality to drop above / below VM ///////////////////////////////////////////// var td = $('').append(td).appendTo(tbl); // VM OS type icon var tr = $(''); if($('#vboxPane').data('vboxConfig').enableCustomIcons && vmn.customIcon) { $(''); var td = $('').addClass('vboxChooserDropTarget').css({'height':'4px'}).append(td).appendTo(tbl); // Context menus? if(vboxChooser._vmContextMenuObj) { $(tbl).contextMenu({ menu: vboxChooser._vmContextMenuObj.menuId(), menusetup : function(el) { if(!$(el).hasClass('vboxListItemSelected')) $(el).trigger('click'); } },function(act,el,pos,d,e){ vboxChooser._vmContextMenuObj.menuClickCallback(act); }); // Open settings on dblclick $(tbl).dblclick(function(){ if(vboxChooser._vmContextMenuObj.menuItems['settings'].enabled()) vboxChooser._vmContextMenuObj.menuItems['settings'].click(); }); } return tbl; }, /* * VM Group Dropped */ vmGroupDropped : function(e, droppedGroup) { $(vboxChooser._anchor).enableSelection(); var vmGroupPath = vboxChooser._draggingGroup; vboxChooser._draggingGroup = false; $(droppedGroup).removeClass('vboxHover'); if(!vboxChooser._editable) return false; // Cannot drag a group that contains a VM without // an unlocked session state if it will modify VM // Groups var sessionLocked = false; if($(droppedGroup).find('td.vboxVMSessionOpen')[0]) sessionLocked=true; // Check for above/below group first var dropTarget = vboxChooser._anchor.find('div.vboxChooserDropTargetHover').first(); if(dropTarget[0]) { // Make sure that this wasn't dropped onto a sub-group or itself if( !dropTarget.closest('div.vboxChooserGroup')[0] || vmGroupPath == dropTarget.closest('div.vboxChooserGroup').data('vmGroupPath') || dropTarget.closest('div.vboxChooserGroup').data('vmGroupPath').indexOf(vmGroupPath + '/') == 0 ) { return; } // If we are not still in the same group, check for name conflict var currParentGroupPath = $(droppedGroup).closest('div.vboxChooserGroup').parent().closest('div.vboxChooserGroup').data('vmGroupPath'); if(dropTarget.closest('div.vboxChooserGroup').parent().closest('div.vboxChooserGroup').data('vmGroupPath') != currParentGroupPath) { // Do not allow to be dragged into another group // if there is a Vm with a locked session in this one if(sessionLocked && !$('#vboxPane').data('vboxConfig')['phpVboxGroups']) return; // Make sure there are no conflicts var groupName = $(droppedGroup).children('div.vboxChooserGroupIdentifier').attr('title'); var newGroupName = groupName; var i = 2; while(vboxChooser.groupNameConflicts(dropTarget.closest('div.vboxChooserGroup').parent(), newGroupName)) { newGroupName = groupName + ' (' + (i++) + ')'; } $(droppedGroup).children('div.vboxChooserGroupIdentifier').attr({'title':newGroupName}) .siblings('div.vboxChooserGroupHeader') .children('span.vboxChooserGroupName').text(newGroupName); } // Insert before or insert after? if(dropTarget.hasClass('vboxDropTargetTop')) { $(droppedGroup).detach().insertBefore(dropTarget.closest('div.vboxChooserGroup')); } else { $(droppedGroup).detach().insertAfter(dropTarget.closest('div.vboxChooserGroup')); } // Dropped onto a group or main VM list } else { // Will not do this if this group contains // a VM with a locked session if(sessionLocked && !$('#vboxPane').data('vboxConfig')['phpVboxGroups']) return; var dropTarget = vboxChooser._anchor.find('div.vboxHover').first(); // Dropped onto a group if(dropTarget[0] && dropTarget.parent().hasClass('vboxChooserGroup')) { dropTarget = dropTarget.parent(); // Make sure that this wasn't dropped onto a sub-group or itself if( vmGroupPath == dropTarget.data('vmGroupPath') || dropTarget.closest('div.vboxChooserGroup').data('vmGroupPath').indexOf(vmGroupPath + '/') == 0 ) { return; } // Dropped onto main vm list } else if($(vboxChooser._anchor).find('div.vboxGroupHover').length == 0 && $(vboxChooser._anchor).hasClass('vboxChooserDropTargetHoverRoot')) { dropTarget = null; // Only showing one group? if(vboxChooser._showOnlyGroupHistory.length > 0) { dropTarget = $(vboxChooser._showOnlyGroupHistory[vboxChooser._showOnlyGroupHistory.length-1]); } if(!$(dropTarget)[0]) dropTarget = vboxChooser._anchor.children('div.vboxChooserGroup'); } else { return; } // Make sure there are no conflicts var newElm = $(droppedGroup).detach(); var groupName = $(droppedGroup).children('div.vboxChooserGroupIdentifier').attr('title'); var newGroupName = groupName; var i = 2; while(vboxChooser.groupNameConflicts(dropTarget, newGroupName, $(newElm).data('vmGroupPath'))) { newGroupName = groupName + ' (' + (i++) + ')'; } $(newElm) .children('div.vboxChooserGroupIdentifier').attr({'title':newGroupName}) .siblings('div.vboxChooserGroupHeader') .children('span.vboxChooserGroupName').text(newGroupName); $(newElm).insertBefore(dropTarget.children('div.vboxChooserGroupVMs')); } // vmGroup dropped - compose and save group definitions vboxChooser.composeGroupDef(true); // Hide group info vboxChooser._anchor.find('div.vboxChooserGroupHeader').trigger('mouseout'); // Resize chooser elements vboxChooser._resizeElements(); vboxChooser.selectionListChanged(vboxChooser._selectedList); }, /* * VM dropped */ vmDropped : function (e, droppedVM){ $(vboxChooser._anchor).enableSelection(); vboxChooser._dragging = null; if(!vboxChooser._editable) return false; // Cannot drag if this VM's session is not open var thisSessionLocked = false; var vmData = vboxVMDataMediator.getVMData($(droppedVM).data('vmid')); if(vmData.sessionState != 'Unlocked') thisSessionLocked = true; // Where was this dropped? var dropTarget = $('#'+vboxChooser._anchorid).find('td.vboxChooserDropTargetHover'); // Dropped above / below a VM if(dropTarget[0]) { // Dropped from another group into this one, // but this group already has this VM if((dropTarget.closest('table').closest('div.vboxChooserGroup').data('vmGroupPath') != $(droppedVM).closest('div.vboxChooserGroup').data('vmGroupPath')) && dropTarget.closest('table').siblings('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+$(droppedVM).data('vmid'))[0]) { return true; } // If session of this VM is locked, don't allow it to be // dragged out of current group if(thisSessionLocked && !$('#vboxPane').data('vboxConfig')['phpVboxGroups'] && ($(droppedVM).closest('div.vboxChooserGroup').data('vmGroupPath') != dropTarget.closest('div.vboxChooserGroup').data('vmGroupPath'))) { return } // Get VM from target's parent table if(dropTarget.hasClass('vboxDropTargetTop')) { if(!e.ctrlKey && !e.metaKey) { $(droppedVM).detach().insertBefore($(dropTarget).closest('table')); } else { // Copy if($(dropTarget).closest('table').parent().children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmData.id)[0]) return; vboxChooser.vmHTML(vmData).insertBefore($(dropTarget).closest('table')); } } else { if(!e.ctrlKey && !e.metaKey) { $(droppedVM).detach().insertAfter($(dropTarget).closest('table')); } else { // Copy - Don't allow if it already exists if($(dropTarget).closest('table').parent().children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmData.id)[0]) return; vboxChooser.vmHTML(vmData).insertAfter($(dropTarget).closest('table')); } } // Not dropped above / below vm } else { // Don't allow this if sessoin is locked if(thisSessionLocked && !$('#vboxPane').data('vboxConfig')['phpVboxGroups']) return; // Dropped ON a vm? dropTarget = $('#'+vboxChooser._anchorid).find('table.vboxHover:not(.ui-draggable-dragging)').first(); if($(dropTarget).data('vmid')) { // Create a group? dropTarget = $('#'+vboxChooser._anchorid).find('table.vboxHover').first(); // Nothing to do. Not dropped on valid target if(!dropTarget[0] || ($(dropTarget).data('vmid') == $(droppedVM).data('vmid'))) return true; // Dont' allow this if target VM's session is locked if($(dropTarget).find('td.vboxVMSessionOpen')[0]) return; // Where to drop vboxChooser.. var p = dropTarget.closest('div.vboxChooserGroup').children('div.vboxChooserGroupVMs'); // assume root? if(!p[0]) p = vboxChooser._anchor.children('div.vboxChooserGroupVMs'); // Determine group name var gname = trans('New group','UIGChooserModel'); var tgname = gname; var i = 2; while(vboxChooser.groupNameConflicts($(p).parent(), tgname)) { tgname = gname + ' ' + (i++); } // New position is below target var ghtml = vboxChooser.groupHTML(String(dropTarget.closest('div.vboxChooserGroup').data('vmGroupPath')+'/'+tgname).replace('//','/')); if(!e.ctrlKey && !e.metaKey) { ghtml.children('div.vboxChooserGroupVMs').append($(droppedVM).detach()); } else { ghtml.children('div.vboxChooserGroupVMs').append(vboxChooser.vmHTML(vmData)); } ghtml.children('div.vboxChooserGroupVMs').append(dropTarget.detach()); ghtml.insertBefore(p); // Dropped in the main VM list or group header? } else { dropTarget = $(vboxChooser._anchor).find('div.vboxHover').first(); if(dropTarget[0] && dropTarget.hasClass('vboxChooserGroupHeader')) { // Group already has this dragging VM? if(dropTarget.siblings('div.vboxChooserGroupVMs').children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+$(droppedVM).data('vmid'))[0]) { return; } if(!e.ctrlKey && !e.metaKey) $(droppedVM).detach().appendTo(dropTarget.siblings('div.vboxChooserGroupVMs').first()); else vboxChooser.vmHTML(vmData).appendTo(dropTarget.siblings('div.vboxChooserGroupVMs').first()); // Main VM list } else if($(vboxChooser._anchor).find('div.vboxGroupHover').length == 0 && $(vboxChooser._anchor).hasClass('vboxChooserDropTargetHoverRoot')) { dropTarget = null; // Only showing one group? if(vboxChooser._showOnlyGroupHistory.length > 0) { dropTarget = $(vboxChooser._showOnlyGroupHistory[vboxChooser._showOnlyGroupHistory.length-1]); } if(!$(dropTarget)[0]) dropTarget = vboxChooser._anchor.children('div.vboxChooserGroup'); // Already in this list? if(dropTarget.children('div.vboxChooserGroupVMs').children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+$(droppedVM).data('vmid'))[0]) { return true; } if(!e.ctrlKey && !e.metaKey) { $(droppedVM).detach().appendTo(dropTarget.children('div.vboxChooserGroupVMs').first()); } else { vboxChooser.vmHTML(vmData).appendTo(dropTarget.children('div.vboxChooserGroupVMs').first()); } } } } // vm dropped - compose and save group definitions vboxChooser.composeGroupDef(true); // Resize chooser elements vboxChooser._resizeElements(); vboxChooser.selectionListChanged(vboxChooser._selectedList); }, /* * Group selected items into a new group */ groupSelectedItems : function() { // Get all group paths to determine new group target var groupPaths = {}; vboxChooser._anchor.find('div.vboxVMGroupSelected').closest('div.vboxChooserGroup').each(function(idx,elm) { groupPaths[$(elm).data('vmGroupPath')] = 1; }); vboxChooser._anchor.find('table.vboxListItemSelected').closest('div.vboxChooserGroup').each(function(idx,elm) { groupPaths[$(elm).data('vmGroupPath')] = 1; }); // The group clsest to the root group will be the target var groupPathTarget = null; for(var i in groupPaths) { if(typeof(i) != 'string') continue; // Already at root group. Nothing to do if(groupPathTarget == '/') break; // No target set yet or equal targets, or this group is the root if(!groupPathTarget || groupPathTarget == i || i == '/') { groupPathTarget = i; continue; } var t1 = groupPathTarget.split("/"); var t2 = i.split("/"); for(var i = 0; i < Math.min(t1.length,t2.length); i++) { if(t1[i] != t2[i]) { groupPathTarget = ''; for(var a = 0; a < i; a++) { groupPathTarget += "/" + t1[a]; } groupPathTarget = groupPathTarget.replace('//','/'); break; } } } var target = vboxChooser.getGroupElement(groupPathTarget, true); if(!$(target)[0]) return; // Determine group name var gname = trans('New group','UIGChooserModel'); var tgname = gname; var i = 2; while(vboxChooser.groupNameConflicts($(target), tgname)) { tgname = gname + ' ' + (i++); } var gHTML = vboxChooser.groupHTML('/'+tgname); // Append group and vm elements vboxChooser._anchor.find('div.vboxVMGroupSelected').detach().insertAfter(gHTML.children('div.vboxChooserGroupHeader')); vboxChooser._anchor.find('table.vboxListItemSelected').detach().appendTo(gHTML.children('div.vboxChooserGroupVMs')); gHTML.insertBefore($(target).children('div.vboxChooserGroupVMs')); // group selected items, // Compose and save group definitions vboxChooser.composeGroupDef(true); // Resize chooser elements vboxChooser._resizeElements(); }, /** * Compose group data from GUI and optionally save it * * @param save - save group definitions to vbox */ composeGroupDef : function(save) { var allGroups = []; var groupsResolved = false; // Keep looping through group definitions until // there are no groups removed while(!groupsResolved) { allGroups = []; groupsResolved = true; vboxChooser._anchor.find('div.vboxChooserGroup:not(.ui-draggable-dragging)').each(function(idx,elm) { // Group element was removed if(!$(elm)[0]) return; // Compose group path var myPath = $(elm).children('div.vboxChooserGroupIdentifier').attr('title'); if(!myPath) myPath = '/'; $(elm).parents('div.vboxChooserGroup:not(.ui-draggable-dragging)').each(function(idx2,elm2){ var pName = $(elm2).children('div.vboxChooserGroupIdentifier').attr('title'); if(!pName) pName = '/'; myPath = String(pName + '/' + myPath).replace('//','/'); }); // Groups var gList = []; $(elm).children('div.vboxChooserGroup:not(.ui-draggable-dragging)').each(function(idx2,elm2){ // If this group is selected, we'll have to update its path // in the selection list var selected = $(elm2).hasClass('vboxVMGroupSelected'); var oldPath = $(elm2).data('vmGroupPath'); var newPath = String(myPath + '/' + $(elm2).children('div.vboxChooserGroupIdentifier').attr('title')).replace('//','/'); gList[gList.length] = $(elm2).children('div.vboxChooserGroupIdentifier').attr('title'); // set / correct group path data $(elm2).data('vmGroupPath', newPath); // Group's path changed? if(selected && (oldPath != newPath)) { for(var i = 0; i < vboxChooser._selectedList.length; i++) { if(vboxChooser._selectedList[i].type == 'group' && vboxChooser._selectedList[i].groupPath == oldPath) { vboxChooser._selectedList[i].groupPath = String(myPath + '/' + $(elm2).children('div.vboxChooserGroupIdentifier').attr('title')).replace('//','/'); break; } } } }); // VMs var vmList = []; $(elm).children('div.vboxChooserGroupVMs').children('table.vboxChooserVM:not(.ui-draggable-dragging)').each(function(idx3,elm3){ vmList[vmList.length] = $(elm3).data('vmid'); }); // Skip and remove if there are no VMs or subgroups // And it is not the parent group if(gList.length + vmList.length == 0 && !$(elm).hasClass('vboxChooserGroupRoot')) { // remove from selected list? if(elm && $(elm).hasClass('vboxVMGroupSelected')) { var myPath = $(elm).data('vmGroupPath'); // Deselect item vboxChooser._selectedList = vboxChooser._selectedList.filter(function(v){ return (v.type != 'group' || (v.groupPath != myPath)); }); } $(elm).empty().remove(); groupsResolved = false; return false; } // append to all groups list gorder = []; if(gList.length) gorder[0] = 'go='+gList.join(',go='); if(vmList.length) gorder[gorder.length] = 'm='+vmList.join(',m='); allGroups[allGroups.length] = { path: $(elm).data('vmGroupPath'), order: gorder.join(',') }; // Update counts span $(elm).children('div.vboxChooserGroupVMs').css({'display':(vmList.length || $(elm).data('vmGroupPath') == '/' ? '' : 'none')}) .siblings('div.vboxChooserGroupHeader') .each(function(hidx,header) { var staticTip = ''+$(header).siblings('div.vboxChooserGroupIdentifier').attr('title')+''+ (gList.length ? ('
' + trans('%n group(s)','UIGChooserItemGroup',gList.length).replace('%n',gList.length)) : '') + (vmList.length ? ('
' + trans('%n machine(s)','UIGChooserItemGroup',vmList.length).replace('%n',vmList.length)) : ''); $(header).tipped({'source':function() { // find number of running VMs var runningVMs = 0; if(vmList.length) { $(header).siblings('div.vboxChooserGroupVMs').find('td.vboxVMSessionOpen').each(function(idx,elm3) { if(vboxVMStates.isRunning(vboxVMDataMediator.getVMData($(elm3).closest('table').data('vmid')))) runningVMs++; }); } return staticTip + (runningVMs > 0 ? ' ' + trans('(%n running)','UIGChooserItemGroup',runningVMs).replace('%n', runningVMs) : ''); } ,'position':'mouse','delay':1500}); }) .children('span.vboxChooserGroupInfo') .children('span.vboxChooserGroupCounts').html( (gList.length ? (''+gList.length) : '') + (vmList.length ? (''+vmList.length) : '') ); }); } // Save GUI group definition? if(!save) return; // Tell the interface we're about to save groups vboxChooser._editable = false; $('#vboxPane').trigger('vmGroupDefsSaving'); vboxChooser._groupDefs = allGroups; // Save machine groups and trigger change var vms = []; var vmList = vboxVMDataMediator.getVMList(); for(var i = 0; i < vmList.length; i++) { if(!vmList[i] || vmList[i].id == 'host') continue; /* If a VM's groups have changed, add it to the list */ var eGroups = vmList[i].groups; var nGroups = vboxChooser.getGroupsForVM(vmList[i].id); if($(nGroups).not(eGroups).length || $(eGroups).not(nGroups).length) { vms[vms.length] = { 'id' : vmList[i].id, 'groups' : nGroups }; } } // Save machines groups? if(vms.length) { // Reload VMs and group definitions var reloadAll = function() { var ml = new vboxLoader(); ml.add('vboxGetMedia',function(d){$('#vboxPane').data('vboxMedia',d.responseData);}); ml.add('vboxGroupDefinitionsGet',function(d){vboxChooser._groupDefs = d.responseData;}); // Reload VM list and group definitions, something went wrong ml.onLoad = function() { // Stop vmlist from refreshing.. vboxChooser.stop(); // reset selections $('#vboxPane').trigger('vmSelectionListChanged',[vboxChooser]); // ask for new one vboxChooser.start(); }; ml.run(); }; $.when(vboxAjaxRequest('machinesSaveGroups',{'vms':vms})).done(function(res){ if(res.responseData.errored) { reloadAll(); vboxChooser._editable = true; $('#vboxPane').trigger('vmGroupDefsSaved'); } else { $.when(vboxAjaxRequest('vboxGroupDefinitionsSet',{'groupDefinitions':allGroups})).always(function(){ vboxChooser._editable = true; $('#vboxPane').trigger('vmGroupDefsSaved'); }); } }).fail(reloadAll); } else { $.when(vboxAjaxRequest('vboxGroupDefinitionsSet',{'groupDefinitions':allGroups})).always(function(){ vboxChooser._editable = true; $('#vboxPane').trigger('vmGroupDefsSaved'); }); } return allGroups; }, /* * Return a list of groups that VM is a member of */ getGroupsForVM : function(vmid) { var gPathList = []; vboxChooser._anchor.find('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmid+':not(.ui-draggable-dragging)').each(function(idx,elm){ var gParent = $(elm).closest('div.vboxChooserGroup'); if(!gParent.hasClass('ui-draggable-dragging')) gPathList[gPathList.length] = gParent.data('vmGroupPath'); }); return gPathList; }, /* * Determine whether or not a group name conflicts * with another group in parent */ groupNameConflicts : function(parentGroup, name, ignoreGroupAtPath) { var found = false; parentGroup.children('div.vboxChooserGroup:not(.ui-draggable-dragging)').children('div.vboxChooserGroupIdentifier[title="'+name+'"]').parent().each(function(i,elm){ if(ignoreGroupAtPath && (ignoreGroupAtPath == $(elm).data('vmGroupPath'))) return true; found=true; return false; }); return found; }, /* * Ungroup selected group */ unGroupSelectedGroup : function() { var target = $(vboxChooser.getSelectedGroupElements()[0]).siblings('div.vboxChooserGroupVMs'); // Groups // - ignore group at path we are currently ungrouping var ignoreGroup = $(vboxChooser.getSelectedGroupElements()[0]).data('vmGroupPath'); $(vboxChooser.getSelectedGroupElements()[0]).children('div.vboxChooserGroup').each(function(i,elm) { // Make sure there are no conflicts var newElm = $(elm).detach(); var groupName = $(elm).children('div.vboxChooserGroupIdentifier').attr('title'); var newGroupName = groupName; var i = 2; while(vboxChooser.groupNameConflicts($(target).parent(), newGroupName, ignoreGroup)) { newGroupName = groupName + ' (' + (i++) + ')'; } $(newElm).children('div.vboxChooserGroupIdentifier').attr({'title':newGroupName}) .siblings('div.vboxChooserGroupHeader') .children('span.vboxChooserGroupName').text(newGroupName); $(newElm).insertBefore(target); }); // VMs $(vboxChooser.getSelectedGroupElements()[0]).children('div.vboxChooserGroupVMs').children().each(function(i,elm){ $(elm).detach(); if(!target.children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+$(elm).data('vmid'))[0]) target.append(elm); }); // ungroup selected items // compose and save group definitions vboxChooser.composeGroupDef(true); // Resize chooser elements vboxChooser._resizeElements(); vboxChooser.selectionListChanged(); }, /* * Sort group sub-elements based on VirtualBox * group definitions */ sortGroup : function(gElm) { // Find group orders var gPath = $(gElm).data('vmGroupPath'); var groupList = vboxChooser._groupDefs; if(!(gPath && groupList)) return; var machineOrder = []; var groupOrder = []; // Get correct order for(var i = 0; i < groupList.length; i++) { if(groupList[i].path == gPath) { order = groupList[i].order.split(','); for(var a = 0; a < order.length; a++) { kv = order[a].split('=',2); if(kv[0] == 'm') machineOrder[machineOrder.length] = kv[1]; else groupOrder[groupOrder.length] = kv[1]; } } } // sort groups var groups = $(gElm).children('div.vboxChooserGroup').get(); var maxPos = groups.length; groups.sort(function(a,b){ var Pos1 = jQuery.inArray($(a).children('div.vboxChooserGroupIdentifier').attr('title'), groupOrder); var Pos2 = jQuery.inArray($(b).children('div.vboxChooserGroupIdentifier').attr('title'), groupOrder); if(Pos1==-1) Pos1 = maxPos; if(Pos2==-1) Pos2 = maxPos; return (Pos1 > Pos2 || Pos1 == -1 ? -1 : (Pos2 == Pos1 ? 0 : 1)); }); $.each(groups, function(idx,itm) { $(itm).insertAfter($(gElm).children('div.vboxChooserGroupHeader')); }); // sort VMs var vms = $(gElm).children('div.vboxChooserGroupVMs').children('table.vboxChooserVM').get(); var maxPos = vms.length; vms.sort(function(a,b) { var Pos1 = jQuery.inArray($(a).data('vmid'), machineOrder); var Pos2 = jQuery.inArray($(b).data('vmid'), machineOrder); if(Pos1==-1) Pos1 = maxPos; if(Pos2==-1) Pos2 = maxPos; return (Pos1 > Pos2 ? 1 : (Pos2 == Pos1 ? 0 : -1)); }); $.each(vms, function(idx,itm) { $(gElm).children('div.vboxChooserGroupVMs').append(itm); }); }, /* * Sort selected group by item names */ sortSelectedGroup : function(rootElm) { var el = $(vboxChooser.getSelectedGroupElements()[0]); if(rootElm || !el) { el = vboxChooser._anchor.children('div.vboxChooserGroup'); } // sort groups var groups = $(el).children('div.vboxChooserGroup').get(); groups.sort(function(a,b){ return $(b).children('div.vboxChooserGroupIdentifier').attr('title').localeCompare($(a).children('div.vboxChooserGroupIdentifier').attr('title')); }); $.each(groups, function(idx,itm) { $(itm).insertAfter($(el).children('div.vboxChooserGroupHeader')); }); // sort VMs var vms = $(el).children('div.vboxChooserGroupVMs').children('table.vboxChooserVM').get(); vms.sort(function(a,b) { return $(a).find('span.vboxVMName').text().localeCompare($(b).find('span.vboxVMName').text()); }); $.each(vms, function(idx,itm) { $(el).children('div.vboxChooserGroupVMs').append(itm); }); // compose and save group definitions vboxChooser.composeGroupDef(true); }, /* * Rename selected group */ renameSelectedGroup : function() { var el = $(vboxChooser.getSelectedGroupElements()[0]); // Function to rename group var renameGroup = function(e, textbox) { if(!textbox) textbox = $(this); var newName = $(textbox).val().replace(/[\\\/:*?"<>,]/g,'_'); if(newName && newName != $(textbox).closest('div.vboxChooserGroup').children('div.vboxChooserGroupIdentifier').attr('title')) { // Do not rename if it conflicts var noConflict = newName; var i = 2; while(vboxChooser.groupNameConflicts($(textbox).parent().parent().parent().parent(), noConflict)) { noConflict = newName + ' (' + (i++) + ')'; } newName = noConflict; $(textbox).closest('div.vboxChooserGroup') .children('div.vboxChooserGroupIdentifier').attr({'title':newName}) .siblings('div.vboxChooserGroupHeader') .children('span.vboxChooserGroupName').html(newName); // group renamed, compose and save groups vboxChooser.composeGroupDef(true); // Write out collapsed group list vboxChooser._saveCollapsedGroups(); } $(textbox).parent().parent().children().css({'display':''}); $(textbox).parent().empty().remove(); }; $(el).children('div.vboxChooserGroupHeader').children().css({'display':'none'}); $(el).children('div.vboxChooserGroupHeader').append( $('
').append( $('').attr({'type':'text','value':$(el).children('div.vboxChooserGroupIdentifier').attr('title')}).css({'width':'90%','padding':'0px','margin':'0px'}).on('keypress',function(e){ if (e.which == 13) { $(this).off('blur', renameGroup); renameGroup(e,this); e.stopPropagation(); e.preventDefault(); $(this).trigger('blur'); return false; } }) ) ); $(el).children('div.vboxChooserGroupHeader').children('form').children('input').focus().select().blur(renameGroup); }, /* * Select a single group */ _selectGroup : function(gelm) { $(gelm).addClass('vboxVMGroupSelected'); }, /* * Deselect a single group */ _deselectGroup : function(gelm) { $(gelm).removeClass('vboxVMGroupSelected'); }, /* * Select (or unselect) an item in our list. Called onmousedown or onCLick */ selectItem : function(e) { // Right click selects item if it is not selected if(e.which != 1) { // Right click on group header and group is selected // just return and show context menu if($(this).hasClass('vboxChooserGroupHeader') && $(this).parent().hasClass('vboxVMGroupSelected')) { return true; // Right click on VM and VM is already selected // just return and show context menu } else if($(this).hasClass('vboxListItemSelected')) { return true; } } var selectedList = []; var item = $(this); // Group? if($(item).hasClass('vboxChooserGroupHeader')) { // No control key. Exclusive selection if(!e.ctrlKey && !e.metaKey) { // already selected if(vboxChooser._selectedList.length == 1 && vboxChooser._selectedList[0].type == 'group' && vboxChooser._selectedList[0].groupPath == $(item).parent().data('vmGroupPath')) return true; vboxChooser._anchor.find('.vboxListItemSelected').removeClass('vboxListItemSelected'); vboxChooser._anchor.find('div.vboxVMGroupSelected') .each(function(idx,gelm) { vboxChooser._deselectGroup(gelm); }); // select current group vboxChooser._selectGroup($(item).parent()); selectedList = [{ type: 'group', groupPath: $(item).parent().data('vmGroupPath') }]; // Already selected, and ctrl key } else if($(item).parent().hasClass('vboxVMGroupSelected')){ // Deselect item selectedList = vboxChooser._selectedList.filter(function(v){ return (v.type != 'group' || (v.groupPath != $(item).parent().data('vmGroupPath'))); }); vboxChooser._deselectGroup($(item).parent()); // Not already selected, and ctrl key } else { vboxChooser._selectGroup($(item).parent()); selectedList = vboxChooser._selectedList; selectedList[selectedList.length] = { type: 'group', groupPath: $(item).parent().data('vmGroupPath') }; } // VM } else { // No ctrl key or selection is host. Exclusive selection if((!e.ctrlKey && !e.metaKey) || $(item).data('vmid') == 'host') { vboxChooser._anchor.find('.vboxListItemSelected').removeClass('vboxListItemSelected'); vboxChooser._anchor.find('div.vboxVMGroupSelected').removeClass('vboxVMGroupSelected') .each(function(idx,gelm){ vboxChooser._deselectGroup(gelm); }); // Select current VM $(item).addClass('vboxListItemSelected').removeClass('vboxHover'); // already selected if(vboxChooser._selectedList.length == 1 && vboxChooser._selectedList[0].type == 'vm' && vboxChooser._selectedList[0].id == $(item).data('vmid')) return true; selectedList = [{ type: 'vm', id: $(item).data('vmid'), groupPath: $(item).parent().data('vmGroupPath') }]; // Already selected, and ctrl key } else if($(item).hasClass('vboxListItemSelected')) { // Deselect item selectedList = vboxChooser._selectedList.filter(function(v){ return (v.type == 'group' || (v.id != $(item).data('vmid'))); }); $(item).removeClass('vboxListItemSelected'); // ctrl key, but not already selected } else { $(item).addClass('vboxListItemSelected').removeClass('vboxHover'); selectedList = vboxChooser._selectedList; selectedList[selectedList.length] = { type: 'vm', id: $(item).data('vmid'), groupPath: $(item).parent().data('vmGroupPath') }; } } // Remove host? if(selectedList.length > 1) { // Deselect host selectedList = selectedList.filter(function(v){ return (v.type == 'group' || (v.id != 'host')); }); vboxChooser._anchor.children('table.vboxChooserItem-'+vboxChooser._anchorid+'-host').removeClass('vboxListItemSelected'); } vboxChooser.selectionListChanged(selectedList); return true; }, /* * Show only single group element identified by gelm */ showOnlyGroupElm : function(gelm) { // Going backwards affects animations var back = false; // gelm is null if we're going backwards if(!gelm) { if(vboxChooser._showOnlyGroupHistory.length > 1) { // this gets rid of current vboxChooser._showOnlyGroupHistory.pop(); // selects previous gelm = vboxChooser._showOnlyGroupHistory.pop(); back = true; } else { gelm = null; } } else { // Hold history vboxChooser._showOnlyGroupHistory[vboxChooser._showOnlyGroupHistory.length] = gelm; } // No scrolling vboxChooser._anchor.css({'overflow-y':'hidden'}); if($(gelm)[0]) { // Slide over or back $.when(vboxChooser._anchor.hide('slide', {direction: (back ? 'right' : 'left'), distance: (vboxChooser._anchor.outerWidth()/1.5)}, 200)).always(function() { /* hide host when showing only a group */ $('table.vboxChooserItem-'+vboxChooser._anchorid+'-host').hide(); /* Undo anything previously performed by this */ vboxChooser._anchor.find('div.vboxChooserGroupHide').removeClass('vboxChooserGroupHide vboxChooserGroupHideShowContainer'); vboxChooser._anchor.find('div.vboxChooserGroupShowOnly').removeClass('vboxChooserGroupShowOnly'); $(gelm).parents('div.vboxChooserGroup').addClass('vboxChooserGroupHide vboxChooserGroupHideShowContainer').siblings().addClass('vboxChooserGroupHide'); vboxChooser._anchor.find('div.vboxChooserGroupRootLevel').removeClass('vboxChooserGroupRootLevel'); $(gelm).addClass('vboxChooserGroupShowOnly vboxChooserGroupRootLevel').siblings().addClass('vboxChooserGroupHide'); $.when(vboxChooser._anchor.show('slide', {direction: (back ? 'left' : 'right'), distance: (vboxChooser._anchor.outerWidth()/1.5)}, 200)) .done(function(){ // Restore scrolling vboxChooser._anchor.css({'overflow-y':'auto'}); // Hide group info $(gelm).find('div.vboxChooserGroupHeader').trigger('mouseout'); // Reset title sizes vboxChooser._resizeElements(); // force redraw of these $(gelm).find('.vboxFitToContainer').css({'display':'none'}).css({'width':'','display':''}); }); }); } else { vboxChooser._showOnlyGroupHistory = []; // Slide back to anchor $.when(vboxChooser._anchor.hide('slide', {direction: 'right', distance: (vboxChooser._anchor.outerWidth()/1.5)}, 200)).always(function() { /* show host when going back to main list */ $('table.vboxChooserItem-'+vboxChooser._anchorid+'-host').show(); vboxChooser._anchor.find('div.vboxChooserGroupHide').removeClass('vboxChooserGroupHide vboxChooserGroupHideShowContainer'); vboxChooser._anchor.find('div.vboxChooserGroupShowOnly').removeClass('vboxChooserGroupShowOnly '); vboxChooser._anchor.find('div.vboxChooserGroupRootLevel').removeClass('vboxChooserGroupRootLevel'); vboxChooser._anchor.children('div.vboxChooserGroupRoot').addClass('vboxChooserGroupRootLevel'); $.when(vboxChooser._anchor.show('slide', {direction: 'left', distance: (vboxChooser._anchor.outerWidth()/1.5)}, 200)) .done(function(){ // Restore scrolling vboxChooser._anchor.css({'overflow-y':'auto'}); // Hide group info vboxChooser._anchor.find('div.vboxChooserGroupHeader').trigger('mouseout'); // Reset title sizes vboxChooser._resizeElements(); // force redraw of these vboxChooser._anchor.find('.vboxFitToContainer').css({'display':'none','width':''}).css({'display':''}); }); }); } }, /* * Return HTML for group */ groupHTML : function(gpath) { if(!gpath) gpath = '/'; var first = gpath == '/'; var gname = gpath.substring(gpath.lastIndexOf('/')+1); var collapsed = vboxChooser._isGroupCollapsed(gpath); var gHTML = $('
').append( $('
').addClass('vboxChooserGroupIdentifier').css({'display':'none'}).attr({'title':gname}) ).append( $('
').addClass('vboxChooserGroupHeader').css({'display':(first ? 'none' : '')}) .attr({'title':gname}) .dblclick(function() { // Already collapsed? var collapsed = $(this).closest('div.vboxChooserGroup').hasClass('vboxVMGroupCollapsed'); // Button rotation function var rotateButton = function(){return true;}; var vboxArrowImage = $(this).find('span.vboxChooserGroupNameArrowCollapse'); if(!($.browser.msie && $.browser.version.substring(0,1) < 9)) { rotateButton = function() { return $('
').animate({left:90},{ duration: 300, step: function(currentStep) { if(!collapsed) currentStep = (90 - currentStep); vboxArrowImage.css({ 'transform':'rotate('+currentStep+'deg)', '-moz-transform': 'rotate('+currentStep+'deg)', '-webkit-transform': 'rotate('+currentStep+'deg)', '-o-transform': 'rotate('+currentStep+'deg)', '-ms-transform': 'rotate('+currentStep+'deg)' }); }, queue: true, complete: function() { vboxArrowImage.css({ 'transform':'', '-moz-transform': '', '-webkit-transform': '', '-o-transform': '', '-ms-transform': '' }); } }); }; } // Run button rotation and toggle class $.when(rotateButton(), $(this).closest('div.vboxChooserGroup').toggleClass('vboxVMGroupCollapsed', ($.browser.msie && $.browser.version.substring(0,1) < 9) ? undefined : 300)).always(function(){ // Write out collapsed group list vboxChooser._saveCollapsedGroups(); // Reset title sizes vboxChooser._resizeElements(); }); }) .append( $('
').addClass('vboxChooserDropTarget') .addClass('vboxDropTargetTop').hover(function(){ if(vboxChooser._draggingGroup) $(this).addClass('vboxChooserDropTargetHover' + (first ? 'ignore' : '')); }, function(){ $(this).removeClass('vboxChooserDropTargetHover'); }) ) .append( $('').addClass('vboxChooserGroupNameArrowLeft vboxChooserGroupNameArrowCollapse vboxArrowImage') .mousedown(function(e){ e.stopPropagation(); e.preventDefault(); return false; }).mouseup(function(){ $(this).closest('div.vboxChooserGroupHeader').trigger('dblclick'); }) ).append( $('').addClass('vboxChooserGroupNameArrowLeft vboxChooserGroupShowOnlyBack vboxArrowImage') .click(function(e) { e.stopPropagation(); e.preventDefault(); vboxChooser.showOnlyGroupElm(); return false; }) ) .append($('').addClass('vboxChooserGroupInfo').html( "" ).append( $('').addClass('vboxChooserGroupShowOnly vboxArrowImage') .click(function(e){ e.stopPropagation(); e.preventDefault(); vboxChooser.showOnlyGroupElm($(this).closest('div.vboxChooserGroup')); return false; }) )) .append($('').html(gname).addClass('vboxChooserGroupName vboxFitToContainer')) .append( $('
').addClass('vboxChooserDropTarget vboxChooserDropTargetBottom') .hover(function(){ if(vboxChooser._draggingGroup) $(this).addClass('vboxChooserDropTargetHover' + (first ? 'ignore' : '')); }, function(){ $(this).removeClass('vboxChooserDropTargetHover'); }) ) .hover(function(){ if(vboxChooser._compact) return; $(this).addClass('vboxHover'); // Resize title and add hover class? if(!$(this).parent().hasClass('vboxChooserGroupRoot')) { // Set width of title to -group info span width var infoWidth = $(this).children('span.vboxChooserGroupInfo').width(); var pWidth = $(this).width(); $(this).children('span.vboxChooserGroupName').css({'max-width':(pWidth-infoWidth-20)+'px'}); } },function(){ // Resize title and remove hover class $(this).removeClass('vboxHover'); if(!$(this).parent().hasClass('vboxChooserGroupRoot')) $(this).children('span.vboxChooserGroupName').css({'max-width':''}); }).on('mousedown',vboxChooser.selectItem) ).addClass((first ? 'vboxChooserGroupRoot vboxChooserGroupRootLevel ' : (collapsed ? 'vboxVMGroupCollapsed ' : '')) + 'vboxChooserGroup') .data({'vmGroupPath':gpath}) .draggable({'cursorAt':{left: -10, top: -10},'helper':function(){ return $(this).clone().addClass('vboxVMGroupCollapsed vboxVMGroupSelected') .children('div.vboxChooserGroupHeader').removeClass('vboxHover').children('.vboxChooserGroupNameArrowCollapse') .hide().closest('div.vboxChooserGroup').css({'width':$(this).width()+'px'}); },'start':function() { if(!$('#vboxPane').data('vboxSession').admin) return false; if(!vboxChooser._editable) return false; vboxChooser._draggingGroup = $(this).data('vmGroupPath'); $(vboxChooser._anchor).disableSelection(); },'stop':function(e) { vboxChooser.vmGroupDropped(e,$(this)); }}).append($('
').addClass('vboxChooserGroupVMs')); // Bottom drop target if(!first) { gHTML.hover(function(){ $(this).addClass('vboxGroupHover'); }, function() { $(this).removeClass('vboxGroupHover'); }).append( $('
').addClass('vboxChooserDropTarget vboxChooserDropTargetBottom') .hover(function(){ if(vboxChooser._draggingGroup) $(this).addClass('vboxChooserDropTargetHover'); }, function(){ $(this).removeClass('vboxChooserDropTargetHover'); }) ); } // Group context menu $(gHTML).contextMenu({ menu: vboxChooser._vmGroupContextMenuObj.menuId(), menusetup: function(el) { $(el).children('div.vboxChooserGroupHeader').trigger('click'); } },function(act,el,pos,d,e){ vboxChooser._vmGroupContextMenuObj.menuClickCallback(act, el); }); return gHTML; }, /* * Return selected VM elements */ getSelectedVMElements : function() { return vboxChooser._anchor.find('table.vboxSelected'); }, /* * Return selected group elements */ getSelectedGroupElements : function() { return vboxChooser._anchor.find('div.vboxVMGroupSelected'); }, /* * Start VM list update */ start : function(anchorid) { // already running? if(vboxChooser._running) return; vboxChooser._running = true; // Where are we drawn? if(anchorid) { vboxChooser._anchorid = anchorid; vboxChooser._anchor = $('#'+anchorid); } // Set group definition key vboxChooser._groupDefinitionKey = $('#vboxPane').data('vboxConfig')['groupDefinitionKey']; // Get collapsed group list vboxChooser._collapsedGroups = vboxGetLocalDataItem($('#vboxPane').data('vboxConfig').key+'-collapsedGroups', true); if(!vboxChooser._collapsedGroups) vboxChooser._collapsedGroups = []; else vboxChooser._collapsedGroups = vboxChooser._collapsedGroups.split(','); // Get groups and machine list. datamediator will start listener $.when(vboxAjaxRequest('vboxGroupDefinitionsGet')).done(function(g) { vboxChooser._groupDefs = g.responseData; $.when(vboxVMDataMediator.getVMList()).done(function(d) { vboxChooser.updateList(d); }); }); }, /* * Stop VM list updates and clear list */ stop : function() { if(!vboxChooser._running) return; vboxChooser._running = false; vboxChooser._anchor.html("
"); // reset vars vboxChooser._versionChecked = false; vboxChooser._selectedList = []; vboxChooser.selectedVMs = []; vboxChooser.selectionMode = vboxSelectionModeNone; } }; $(document).ready(function(){ // Calculate scrollbar width vboxChooser._scrollbarWidth = getScrollbarWidth(); // "Stop" chooser $('#vboxPane').on('hostChange',function(){ vboxChooser.stop(); }).on('hostChanged',function(){ vboxChooser.start(); // Refresh menus }).on('vmGroupDefsSaving vmGroupDefsSaved vmSelectionListChanged', function() { if(vboxChooser._vmGroupContextMenuObj) vboxChooser._vmGroupContextMenuObj.update(vboxChooser); if(vboxChooser._vmContextMenuObj) vboxChooser._vmContextMenuObj.update(vboxChooser); // Event list queue }).on('vboxEvents',function(e, eventList) { var redrawVMs = []; var sortGroups = []; var groupsChanged = false; var selectedChanged = false; var resizeElements = false; for(var i = 0; i < eventList.length; i++) { switch(eventList[i].eventType) { //////////////////////////////// // // Machine data changed // //////////////////////////////// case 'OnMachineDataChanged': // Shorthand var vmid = eventList[i].machineId; var data = vboxVMDataMediator.getVMData(vmid); // Update VM in list if(data) { // Enforce VM ownership if($('#vboxPane').data('vboxConfig').enforceVMOwnership && !$('#vboxPane').data('vboxSession').admin && data.owner != $('#vboxPane').data('vboxSession').user) { break; } redrawVMs[redrawVMs.length] = vmid; // Make sure VM has root group at least if(data.groups.length == 0) data.groups = ['/']; // Remove from groups if they have changed var currGroups = vboxChooser.getGroupsForVM(vmid); var groupDiff = $(currGroups).not(data.groups); groupsChanged = groupDiff.length; for(var a = 0; a < groupDiff.length; a++) { var gElm = vboxChooser.getGroupElement(groupDiff[a], false); if(!$(gElm)[0]) return; selectedChanged = (selectedChanged || $(gElm).children('div.vboxChooserGroupVMs').closest('div.vboxVMGroupSelected').length); $(gElm).children('div.vboxChooserGroupVMs') .children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+data.id).empty().remove(); } // Add to other groups var groupDiff = $(data.groups).not(currGroups); groupsChanged = (groupsChanged || groupDiff.length); for(var a = 0; a < groupDiff.length; a++) { var gElm = vboxChooser.getGroupElement(groupDiff[a]); // Skip it if it is already there if($(gElm).children('div.vboxChooserGroupVMs').children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+data.id)[0]) continue; $(gElm).children('div.vboxChooserGroupVMs') .append( vboxChooser.vmHTML(data) ); selectedChanged = (selectedChanged || $(gElm).children('div.vboxChooserGroupVMs').closest('div.vboxVMGroupSelected').length); // Sort the group this machine was added to sortGroups = sortGroups.concat(data.groups); } resizeElements = (resizeElements || groupsChanged); } break; ///////////////////////////////// // // Snapshot taken / deleted / restored // ///////////////////////////////// case 'OnSnapshotDeleted': case 'OnSnapshotTaken': case 'OnSnapshotRestored': case 'OnSnapshotChanged': redrawVMs[redrawVMs.length] = eventList[i].machineId; break; ///////////////////////////////////// // // Machine registered or unregistered // ////////////////////////////////////// case 'OnMachineRegistered': // Shorthand var vmid = eventList[i].machineId; // Unregistered if(!eventList[i].registered) { var wasSelected = vboxChooser.isVMSelected(vmid); $('#'+vboxChooser._anchorid +' table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmid).remove(); groupsChanged = true; // See if VM was selected if(wasSelected) { selectedChanged = true; vboxChooser._selectedList = vboxChooser._selectedList.filter(function(v){ return (v.type == 'group' || (v.id != vmid)); }); } resizeElements = true; break; } // Registered // Enforce VM ownership if($('#vboxPane').data('vboxConfig').enforceVMOwnership && !$('#vboxPane').data('vboxSession').admin && eventList[i].enrichmentData.owner != $('#vboxPane').data('vboxSession').user) { break; } // Add to list vboxChooser.updateVMElement(eventList[i].enrichmentData, true); resizeElements = true; break; /////////////////////////////////// // // Extra data changed // //////////////////////////////////// case 'OnExtraDataChanged': if(!eventList[i].machineId && eventList[i].key.indexOf(vboxChooser._groupDefinitionKey) === 0) { var path = eventList[i].key.substring(vboxChooser._groupDefinitionKey.length); if(!path) path = "/"; var name = path.substring(path.lastIndexOf('/')+1); var vboxVMGroups = vboxChooser._groupDefs; var found = false; // No current group definitions? if(!vboxVMGroups) break; // Step through each group, comparing for(var a = 0; a < vboxVMGroups.length; a++) { if(vboxVMGroups[a].path == path) { // Sort this group if it is different if(vboxVMGroups[a].order != eventList[i].value) sortGroups[sortGroups.length] = path; found = true; vboxVMGroups[a] = {'path':path,'name':name,'order':eventList[i].value}; break; } } // Add to group if not found if(!found) { vboxVMGroups[vboxVMGroups.length] = {'path':path,'name':name,'order':eventList[i].value}; sortGroups[sortGroups.length] = path; // sort when added resizeElements = true; } } else { switch(eventList[i].key) { // redraw when custom icon changes case 'phpvb/icon': redrawVMs[redrawVMs.length] = eventList[i].machineId; break; } } break; //////////////////////////////////////// // // Session or state change gets redrawn // /////////////////////////////////////// case 'OnSessionStateChanged': case 'OnMachineStateChanged': redrawVMs[redrawVMs.length] = eventList[i].machineId; break; } // > } // // Now redraw each VM /////////////////////////// var redrawn = {}; var updateMenus = false; for(var i = 0; i < redrawVMs.length; i++) { if(redrawn[redrawVMs[i]]) continue; redrawn[redrawVMs[i]] = true; vboxChooser.updateVMElement(vboxVMDataMediator.getVMData(redrawVMs[i])); // Update menus if the VM is selected updateMenus = (updateMenus || vboxChooser.isVMSelected(redrawVMs[i])); } // Sort groups var groupsSorted = {}; for(var i = 0; i < sortGroups.length; i++) { if(groupsSorted[sortGroups[i]]) continue; groupsSorted[sortGroups[i]] = true; var gElm = $(vboxChooser.getGroupElement(sortGroups[i]),false); if(gElm[0]) vboxChooser.sortGroup(gElm); } // Groups changed if(groupsChanged || sortGroups.length) { vboxChooser.composeGroupDef(); } // update selection list if(selectedChanged) { vboxChooser.selectionListChanged(vboxChooser._selectedList); } else if(updateMenus) { if(vboxChooser._vmGroupContextMenuObj) vboxChooser._vmGroupContextMenuObj.update(vboxChooser); if(vboxChooser._vmContextMenuObj) vboxChooser._vmContextMenuObj.update(vboxChooser); } if(resizeElements) vboxChooser._resizeElements(true); }); });
').attr({'colspan':'2'}).addClass('vboxChooserDropTarget vboxDropTargetTop'); if(vmn.id != 'host') { td.hover(function(){ if(vboxChooser._dragging && vboxChooser._dragging != vmn.id) $(this).addClass('vboxChooserDropTargetHover'); },function(){ $(this).removeClass('vboxChooserDropTargetHover'); } ); } $('
').attr({'rowspan':'2'}).html("").appendTo(tr); } else { $('').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('
    '+$('').text(vmn.name).html()+''+ (vmn.currentSnapshotName ? ' (' + $('').text(vmn.currentSnapshotName).html() + ')' : '')+'
    '); // Table gets tool tips tip = trans(vboxChooser._vmToolTip, 'UIVMListView').replace('%1',(''+$('') .text(vmn.name).html()+''+(vmn.currentSnapshotName ? ' (' + $('') .text(vmn.currentSnapshotName).html() + ')' : ''))) .replace('%2',trans(vboxVMStates.convert(vmn.state),'VBoxGlobal')) .replace('%3',vboxDateTimeString(vmn.lastStateChange)) .replace('%4',trans(vmn.sessionState,'VBoxGlobal').toLowerCase()); $(tbl).tipped({'source':tip,'position':'mouse','delay':1500}); } $(tr).append(td).appendTo(tbl); // VM state row var tr = $('
').attr({'class':(vmn.id != 'host' && vmn.sessionState != 'Unlocked' ? 'vboxVMSessionOpen' : '')}); // Add VirtualBox version if hosting if(vmn.id == 'host') { $(td).html("
" + trans(vboxVMStates.convert(vmn.state),'VBoxGlobal') + ' - ' + $('#vboxPane').data('vboxConfig').version.string+'
'); // 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

",{'width':'auto'}); } } } else { $(td).html("
" + trans(vboxVMStates.convert(vmn.state),'VBoxGlobal') + '
'); } $(tr).append(td).appendTo(tbl); // Droppable targets var td = $('
').attr({'colspan':'2'}).addClass('vboxChooserDropTarget vboxDropTargetBottom'); if(vmn.id != 'host') { td.hover(function(){ if(vboxChooser._dragging && vboxChooser._dragging != vmn.id) $(this).addClass('vboxChooserDropTargetHover'); },function(){ $(this).removeClass('vboxChooserDropTargetHover'); } ); } $('