You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

tabVMSnapshots.html 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919
  1. <!--
  2. VM Snapshots Pane
  3. Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com)
  4. $Id: tabVMSnapshots.html 595 2015-04-17 09:50:36Z imoore76 $
  5. -->
  6. <div id='vboxTabVMSnapshots' class='vboxInvisible' style='display: none; width:100%;'>
  7. <table class='vboxInvisible' style='height: 99%; width: 99%'>
  8. <tr style='vertical-align: top; height: 1%'>
  9. <td><div id='vboxSnapshotToolbar'></div></td>
  10. </tr>
  11. <tr style='vertical-align: top;'>
  12. <td><ul style='min-height: 400px' class='vboxBordered vboxTreeView' id='vboxSnapshotList'></ul></td>
  13. </tr>
  14. </table>
  15. <!--
  16. New Snapshot Dialog
  17. -->
  18. <div id='vboxSnapshotNew' class='vboxDialogContent' style='display: none;'>
  19. <table class='vboxVertical'>
  20. <tr style='vertical-align: top'>
  21. <th>
  22. <img id='vboxSnapshotNewImg' src='images/vbox/os_other.png' height='32' width='32' />
  23. </th>
  24. <td>
  25. <div style='height: 100%'>
  26. <div class='translate'>Snapshot Name</div>
  27. <input id='vboxSnapshotNewName' style='width: 100%'/>
  28. <div class='translate'>Snapshot Description</div>
  29. <textarea rows='10' id='vboxSnapshotNewDesc' style='width: 100%;'></textarea>
  30. </div>
  31. </td>
  32. </tr>
  33. </table>
  34. </div>
  35. <!--
  36. Snapshot Details Dialog
  37. -->
  38. <div id='vboxSnapshotDetails' class='vboxDialogContent' style='display: none;'>
  39. <table class='vboxVertical'>
  40. <tr>
  41. <th><span class='translate'>Name:</span></th>
  42. <td style='width:100%'>
  43. <input id='vboxSnapshotDetailsName' style='width: 100%'/>
  44. </td>
  45. <td rowspan='2' id='vboxSnapshotSS' style='width:1%'></td>
  46. </tr>
  47. <tr>
  48. <th><span class='translate'>Taken:</span></th>
  49. <td style='width:100%'>
  50. <span id='vboxSnapshotDetailsTaken'></span>
  51. </td>
  52. </tr>
  53. <tr>
  54. <th><span class='translate'>Description:</span></th>
  55. <td colspan='2'>
  56. <textarea rows='12' id='vboxSnapshotDetailsDesc' name='vboxSnapshotDetailsDescElm'></textarea>
  57. </td>
  58. </tr>
  59. <tr>
  60. <th><span class='translate'>Details:</span></th>
  61. <td class='vboxSnapshotDetailsMachine' colspan='2'>
  62. <div id='vboxSnapshotDetailsVM' style='overflow: auto; height: 100%'></div>
  63. </td>
  64. </tr>
  65. </table>
  66. </div>
  67. <script type='text/javascript'>
  68. vboxInitDisplay('vboxSnapshotNew','VBoxTakeSnapshotDlg');
  69. vboxInitDisplay('vboxSnapshotDetails','VBoxSnapshotDetailsDlg');
  70. var vboxSnapshotButtons = [
  71. {
  72. 'name' : 'take_snapshot',
  73. 'label' : 'Take Snapshot...',
  74. 'language_context': 'UIActionPool',
  75. 'icon' : 'snapshot_take',
  76. 'enabled' : function(item) {
  77. if(typeof item == 'string') state = item;
  78. else if(item && $(item).data('vboxSnapshot')) state = $(item).data('vboxSnapshot').state;
  79. else return false;
  80. var vm = vboxChooser.getSingleSelected();
  81. return (item && state == 'current' && jQuery.inArray(vm.state, ['RestoringSnapshot','LiveSnapshotting','DeletingSnapshot']) == -1);
  82. },
  83. 'click' : function (callback) {
  84. var vm = vboxChooser.getSingleSelected();
  85. $('#vboxSnapshotNewImg').attr('src',"images/vbox/" + vboxGuestOSTypeIcon(vm.OSTypeId));
  86. var snRegEx = new RegExp('^' + trans('Snapshot %1','VBoxSnapshotsWgt').replace('%1','([0-9]+)') + '$');
  87. // Get max snapshot name
  88. var snMax = 0;
  89. var snList = $('#vboxSnapshotList').find('li');
  90. for(var i = 0; i < snList.length; i++) {
  91. var snNum = snRegEx.exec($(snList[i]).data('vboxSnapshot').name);
  92. if(snNum) snMax = Math.max(parseInt(snNum[1]), snMax);
  93. }
  94. $('#vboxSnapshotNewName').val(trans('Snapshot %1','VBoxSnapshotsWgt').replace('%1',(snMax+1)));
  95. $('#vboxSnapshotNewName').select();
  96. $('#vboxSnapshotNewDesc').val('');
  97. var buttons = {};
  98. var OKBtn = buttons[trans('OK','QIMessageBox')] = function() {
  99. // Get fresh VM state when this is clicked
  100. var vm = vboxChooser.getSingleSelected();
  101. if(!vm) return;
  102. // Deferred object that will trigger
  103. // taking a snapshot on success
  104. var isPausedOrNotRunning = $.Deferred();
  105. // Take snapshot function when machine is in
  106. // a valid paused or not running state
  107. $.when(isPausedOrNotRunning).done(function(paused) {
  108. var l = new vboxLoader('snapshotTake');
  109. l.add('snapshotTake',function(d){
  110. if(d && d.responseData && d.responseData.progress) {
  111. vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(pres){
  112. // Unpause machine if it was paused
  113. if(paused) {
  114. vboxAjaxRequest('machineSetState',{'vm':vm.id,'state':'resume'});
  115. }
  116. // If progress operation errored, refresh snapshot list
  117. if(pres && !pres.success)
  118. $.when(vboxAjaxRequest('machineGetSnapshots',{'vm':vm.id})).done(__vboxTabSnapshotsFill);
  119. // callback passed to click()? else Refresh vm list
  120. if(typeof callback == 'function') { callback(pres); }
  121. },'progress_snapshot_create_90px.png', trans('Take a snapshot of the current virtual machine state','VBoxSnapshotsWgt'),
  122. vm.name);
  123. } else {
  124. // Unpause machine if it was paused
  125. if(paused) {
  126. vboxAjaxRequest('machineSetState',{'vm':vm.id,'state':'resume'});
  127. }
  128. if(d && d.error) vboxAlert(d.error);
  129. $.when(vboxAjaxRequest('machineGetSnapshots',{'vm':vm.id})).done(__vboxTabSnapshotsFill);
  130. }
  131. },{'vm':vm.id,'name':$('#vboxSnapshotNewName').val(),'description':$('#vboxSnapshotNewDesc').val()});
  132. l.run();
  133. }).fail(function(){
  134. $.when(vboxAjaxRequest('machineGetSnapshots',{'vm':vm.id})).done(__vboxTabSnapshotsFill);
  135. });
  136. // Set to paused state if VM is running
  137. if(vboxVMStates.isRunning(vm)) {
  138. var pl = new vboxLoader('machineSetStatePaused');
  139. pl.add('machineSetState', function(d) {
  140. if(d && d.success) isPausedOrNotRunning.resolve(true);
  141. else isPausedOrNotRunning.reject();
  142. },{'vm':vm.id,'state':'pause'});
  143. pl.run();
  144. } else {
  145. isPausedOrNotRunning.resolve();
  146. }
  147. $(this).dialog('close');
  148. };
  149. buttons[trans('Cancel','QIMessageBox')] = function() {
  150. $(this).dialog('close');
  151. if(typeof callback == 'function') { callback({success:false,uicancel:true}); }
  152. };
  153. $('#vboxSnapshotNewName').off('keypress').on('keypress',function(e) { if (e.keyCode == 13) OKBtn.apply($('#vboxSnapshotNew')); });
  154. $('#vboxSnapshotNew').dialog({'closeOnEscape':false,'width':'400px','height':'auto','buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent','title':'<img src="images/vbox/snapshot_take_16px.png" class="vboxDialogTitleIcon" height="16" width="16" /> ' + trans('Take Snapshot of Virtual Machine','VBoxTakeSnapshotDlg')});
  155. }
  156. },
  157. {
  158. 'name' : 'discard_cur_state',
  159. 'label' : 'Restore Snapshot',
  160. 'icon' : 'snapshot_restore',
  161. 'enabled' : function(item) {
  162. var vm = vboxChooser.getSingleSelected();
  163. return ( item && $(item).data('vboxSnapshot') && $(item).data('vboxSnapshot') && $(item).data('vboxSnapshot').name && $(item).data('vboxSnapshot').state != 'current');
  164. },
  165. 'click' : function () {
  166. var vm = vboxChooser.getSingleSelected();
  167. var snapshot = $('#vboxSnapshotList').find('div.vboxListItemSelected').first().parent().data('vboxSnapshot');
  168. var buttons = {};
  169. var q = '';
  170. // Check if the current state is modified
  171. if(vm.currentStateModified) {
  172. q = trans("<p>You are about to restore snapshot <nobr><b>%1</b></nobr>.</p>" +
  173. "<p>You can create a snapshot of the current state of the virtual machine first by checking the box below; " +
  174. "if you do not do this the current state will be permanently lost. Do you wish to proceed?</p>",'UIMessageCenter');
  175. q += '<p><label><input type="checkbox" id="vboxRestoreSnapshotCreate" checked /> ' + trans('Create a snapshot of the current machine state','UIMessageCenter') + '</label></br>';
  176. q += '<label><input type="checkbox" id="vboxRestoreSnapshotAutoStart" '+ (vboxVMStates.isRunning(vm)? 'checked' : '') + ' /> ' + trans('Automatically start the machine after restore','UIMessageCenter') + '</label></p>';
  177. buttons[trans('Restore','UIMessageCenter')] = function() {
  178. var self = this;
  179. var snautostart = function() {
  180. var l = new vboxLoader();
  181. l.add('machineSetState',function(d){
  182. if(!(d && d.success) && errorMsg) {
  183. vboxAlert(errorMsg.replace('%1', vm.name));
  184. return;
  185. }
  186. // check for progress operation
  187. if(d && d.responseData && d.responseData.progress) {
  188. vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(d){
  189. // Do Nothing
  190. },'progress_state_restore_90px.png',trans('Power on virtual machine','VBoxSnapshotsWgt'), vm.name);
  191. return;
  192. }
  193. },{'vm':vm.id,'state':'powerUp'});
  194. l.run();
  195. };
  196. var snrestore = function(autoStart,takeSnapshot){
  197. // Don't do anything if taking a snapshot failed
  198. if(takeSnapshot && !takeSnapshot.success) {
  199. if (takeSnapshot.uicancel)
  200. vboxSnapshotButtons[1].click();
  201. return;
  202. }
  203. // Power off VM if needed
  204. if(vboxVMStates.isRunning(vm) || vboxVMStates.isPaused(vm)) {
  205. var l = new vboxLoader();
  206. l.add('machineSetState',function(d){
  207. if(!(d && d.success) && errorMsg) {
  208. vboxAlert(errorMsg.replace('%1', vm.name));
  209. return;
  210. }
  211. // check for progress operation
  212. if(d && d.responseData && d.responseData.progress) {
  213. vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(d){
  214. if (d && d.responseData && d.responseData.info) {
  215. // when poweroff completed
  216. if (d.responseData.info.completed) {
  217. // schedule snapshot restore immediate after
  218. setTimeout(snrestore.bind(self,autoStart,takeSnapshot),0);
  219. }
  220. }
  221. },'progress_poweroff_90px.png',trans('Power off virtual machine','VBoxSnapshotsWgt'), vm.name);
  222. }
  223. },{'vm':vm.id,'state':'powerDown'});
  224. l.run();
  225. return;
  226. }
  227. var l = new vboxLoader();
  228. l.add('snapshotRestore',function(d){
  229. if(d && d.responseData && d.responseData.progress) {
  230. vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(d){
  231. if (d && d.responseData && d.responseData.info) {
  232. // when restore completed and auto start is requested
  233. if (d.responseData.info.completed && autoStart) {
  234. // schedule start immediately
  235. setTimeout(snautostart.bind(self), 0);
  236. }
  237. }
  238. },'progress_snapshot_restore_90px.png',trans('Restore Snapshot','VBoxSnapshotsWgt'),
  239. vm.name);
  240. } else if(d && d.error) {
  241. vboxAlert(d.error);
  242. }
  243. },{'vm':vm.id,'snapshot':snapshot.id});
  244. l.run();
  245. };
  246. var vmRestoreAutoStart = $('#vboxRestoreSnapshotAutoStart').prop('checked');
  247. if($('#vboxRestoreSnapshotCreate').prop('checked')) {
  248. vboxSnapshotButtons[0].click(snrestore.bind(self,vmRestoreAutoStart));
  249. } else {
  250. snrestore(vmRestoreAutoStart);
  251. }
  252. $(this).empty().remove();
  253. };
  254. } else {
  255. q = trans('<p>Are you sure you want to restore snapshot <nobr><b>%1</b></nobr>?</p>','UIMessageCenter');
  256. buttons[trans('Restore','UIMessageCenter')] = function() {
  257. var l = new vboxLoader();
  258. l.add('snapshotRestore',function(d){
  259. if(d && d.responseData && d.responseData.progress) {
  260. vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(){
  261. // Let events get picked up. Nothing to do here
  262. },'progress_snapshot_restore_90px.png',trans('Restore Snapshot','VBoxSnapshotsWgt'),
  263. vm.name);
  264. } else if(d && d.error) {
  265. vboxAlert(d.error);
  266. }
  267. },{'vm':vm.id,'snapshot':snapshot.id});
  268. $(this).empty().remove();
  269. l.run();
  270. };
  271. }
  272. vboxConfirm(q.replace('%1',$('<div />').text(snapshot.name).html()),buttons);
  273. },
  274. 'separator' : true
  275. },
  276. {
  277. 'name' : 'delete_snapshot',
  278. 'label' : 'Delete Snapshot',
  279. 'icon' : 'snapshot_delete',
  280. 'enabled' : function(item) {
  281. return (item && $(item).data('vboxSnapshot') && $(item).data('vboxSnapshot').name && $(item).data('vboxSnapshot').state != 'current' && $(item).data('vboxSnapshot').children.length <= 1);
  282. },
  283. 'click' : function () {
  284. var vm = vboxChooser.getSingleSelected();
  285. var snapshot = $('#vboxSnapshotList').find('div.vboxListItemSelected').first().parent().data('vboxSnapshot');
  286. var buttons = {};
  287. buttons[trans('Delete','UIMessageCenter')] = function() {
  288. var l = new vboxLoader();
  289. l.add('snapshotDelete',function(d){
  290. if(d && d.responseData && d.responseData.progress) {
  291. vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(){
  292. // Let events get picked up. Nothing to do here
  293. },'progress_snapshot_discard_90px.png',trans('Delete Snapshot','VBoxSnapshotsWgt'),
  294. vm.name + ' - ' + snapshot.name);
  295. }
  296. },{'vm':vm.id,'snapshot':snapshot.id});
  297. $(this).empty().remove();
  298. l.run();
  299. };
  300. vboxConfirm(trans('<p>Deleting the snapshot will cause the state information saved in it to be lost, and '+
  301. 'storage data spread over several image files that VirtualBox has created together with the snapshot '+
  302. 'will be merged into one file. This can be a lengthy process, and the information in the snapshot cannot '+
  303. 'be recovered.</p></p>Are you sure you want to delete the selected snapshot <b>%1</b>?</p>','UIMessageCenter').replace('%1',$('<div />').text(snapshot.name).html()),buttons);
  304. }
  305. },
  306. {
  307. 'name' : 'show_snapshot_details',
  308. 'label' : 'Show Details',
  309. 'icon' : 'snapshot_show_details',
  310. 'enabled' : function(item) {
  311. return (item && $(item).data('vboxSnapshot') && $(item).data('vboxSnapshot').name && $(item).data('vboxSnapshot').state != 'current');
  312. },
  313. 'click' : function () {
  314. // Current snapshot
  315. var snapshot = $('#vboxSnapshotList').find('div.vboxListItemSelected').first().parent().data('vboxSnapshot');
  316. var vm = vboxChooser.getSingleSelected();
  317. var l = new vboxLoader();
  318. l.add('snapshotGetDetails',function(d){
  319. $('#vboxSnapshotDetailsName').val(d.responseData.name);
  320. $('#vboxSnapshotDetailsTaken').html(vboxDateTimeString(d.responseData.timeStamp));
  321. $('#vboxSnapshotDetailsDesc').val(d.responseData.description);
  322. if(d.responseData.online) {
  323. $('#vboxSnapshotSS').html('<a href="'+ vboxEndpointConfig.screen +'?vm='+vm.id+
  324. '&snapshot='+d.responseData.id+'&full=1" target="_blank"><img src="'+vboxEndpointConfig.screen+'?vm='+
  325. vm.id+'&snapshot='+d.responseData.id+'" /></a>').show();
  326. } else {
  327. $('#vboxSnapshotSS').empty().hide();
  328. }
  329. // Display details
  330. $('#vboxSnapshotDetailsVM').empty();
  331. // Enclosing details Table
  332. var vboxDetailsTable = $('<table />').attr({'class':'vboxDetailsTable'});
  333. // Set to isSnapshot
  334. d.responseData.machine._isSnapshot = true;
  335. for(var i in vboxVMDetailsSections) {
  336. section = vboxVMDetailsSections[i];
  337. if(section.noSnapshot) continue;
  338. $('<tr />').attr({'class':'vboxDetailsHead'}).append(
  339. $('<th />').attr({'class':'vboxDetailsSection','colspan':'2'}).disableSelection()
  340. .html("<img style='float:left; margin-right: 3px; ' src='images/vbox/" + section.icon + "' height='16' width='16' /> ")
  341. .append(
  342. $('<span />').css({'float':'left'}).append(document.createTextNode(trans(section.title, section.language_context) +' '))
  343. )
  344. ).appendTo(vboxDetailsTable);
  345. __vboxDetailAddRows(d.responseData.machine, section.rows, vboxDetailsTable);
  346. }
  347. $('#vboxSnapshotDetailsVM').append(vboxDetailsTable);
  348. },{'vm':vm.id,'snapshot':snapshot.id});
  349. l.onLoad = function(){
  350. var buttons = {};
  351. buttons[trans('OK','QIMessageBox')] = function() {
  352. // Current snapshot
  353. var snapshot = $('#vboxSnapshotList').find('div.vboxListItemSelected').first().parent().data('vboxSnapshot');
  354. var l = new vboxLoader();
  355. l.add('snapshotSave',function(d){
  356. // Let events get picked up. Nothing to do here
  357. },{'vm':vm.id,'snapshot':snapshot.id,'name':$('#vboxSnapshotDetailsName').val(),'description':$('#vboxSnapshotDetailsDesc').val()});
  358. $(this).dialog('close');
  359. l.run();
  360. };
  361. buttons[trans('Cancel','QIMessageBox')] = function(){
  362. $(this).dialog('close');
  363. };
  364. $('#vboxSnapshotDetails').dialog({'closeOnEscape':false,'width':'600px','height':'auto','buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent','title':'<img src="images/vbox/snapshot_show_details_16px.png" class="vboxDialogTitleIcon" /> '+trans('Details of %1 (%2)','VBoxSnapshotDetailsDlg').replace('%1',$('<div />').text(snapshot.name).html()).replace('%2',vm.name)});
  365. };
  366. l.run();
  367. }
  368. },
  369. {
  370. 'name' : 'clone',
  371. 'label' : 'Clone...',
  372. 'language_context': 'UIActionPool',
  373. 'icon' : 'vm_clone',
  374. 'separator' : true,
  375. 'enabled' : function(item) {
  376. var vm = vboxChooser.getSingleSelected();
  377. return (item && $(item).data('vboxSnapshot') && $(item).data('vboxSnapshot').name && !vboxVMStates.isPaused(vm) && !vboxVMStates.isRunning(vm));
  378. },
  379. 'click' : function () {
  380. var vm = vboxChooser.getSingleSelected();
  381. // Current snapshot
  382. var snapshot = $('#vboxSnapshotList').find('div.vboxListItemSelected').first().parent().data('vboxSnapshot');
  383. new vboxWizardCloneVMDialog({'vm':vm,'snapshot':(snapshot.state == 'current' ? undefined : snapshot)}).run();
  384. }
  385. },
  386. ];
  387. /* Append Top Toolbar */
  388. var vboxSnapshotToolbar = new vboxToolbarSmall({buttons: vboxSnapshotButtons, size: 22, language_context: 'VBoxSnapshotsWgt'});
  389. // special case for 'clone' button because it is 16px rather than 22px
  390. vboxSnapshotToolbar.addButtonCSS('clone', {'background-position':'6px 4px'});
  391. vboxSnapshotToolbar.renderTo('vboxSnapshotToolbar');
  392. vboxInitDisplay('vboxSnapshotToolbar','VBoxSnapshotsWgt');
  393. // Context menu for snapshots
  394. var vboxSnapshotContextMenu = new vboxMenu({name:'vboxSnapshotContextMenu', language_context: 'VBoxSnapshotsWgt'});
  395. vboxSnapshotContextMenu.addMenu(vboxSnapshotButtons.slice(-(vboxSnapshotButtons.length-1)));
  396. //Context menu for current state
  397. var vboxSnapshotContextMenuCurrent = new vboxMenu({name: 'vboxSnapshotContextMenuCurrent', language_context: 'VBoxSnapshotsWgt'});
  398. vboxSnapshotContextMenuCurrent.addMenu([vboxSnapshotButtons[0],vboxSnapshotButtons[(vboxSnapshotButtons.length-2)]]);
  399. /* Toolbar and menu updates*/
  400. $('#vboxSnapshotList').on('select',function(e,item) {
  401. // Update toolbar
  402. vboxSnapshotToolbar.update(item);
  403. vboxSnapshotContextMenu.update(item);
  404. vboxSnapshotContextMenuCurrent.update(item);
  405. });
  406. // Hold timer and date vars
  407. vboxSnapshotToolbar._timer = null;
  408. vboxSnapshotToolbar._timeSpans = new Array();
  409. vboxSnapshotToolbar._timeSpans['days'] = 86400;
  410. vboxSnapshotToolbar._timeSpans['hours'] = 3600,
  411. vboxSnapshotToolbar._timeSpans['minutes'] = 60,
  412. vboxSnapshotToolbar._timeSpans['seconds'] = 1;
  413. vboxSnapshotToolbar._timeSpans.sort(function(a,b){return (a > b ? -1 : 1);});
  414. /* Selected VM changed */
  415. $('#vboxPane').on('vmSelectionListChanged',function(){
  416. $('#vboxTabVMSnapshotsTitle').html(trans('Snapshots','UIVMDesktop'));
  417. var vm = vboxChooser.getSingleSelected();
  418. $('#vboxSnapshotList').trigger('select',null);
  419. // Got vm and it's not host
  420. if(vm && vm.id != 'host') {
  421. // Enable tab
  422. $('#vboxTabVMSnapshots').parent().trigger('enableTab', ['vboxTabVMSnapshots']);
  423. $.when(vboxVMDataMediator.getVMDetails(vm.id)).done(function(vm) {
  424. $('#vboxTabVMSnapshotsTitle').html(trans('Snapshots','UIVMDesktop') + (vm && vm.snapshotCount ? trans(' (%1)','VBoxSnapshotsWgt').replace('%1',vm.snapshotCount):''));
  425. });
  426. // Unset last vm
  427. $('#vboxTabVMSnapshots').data('lastVM',0);
  428. // Remove children
  429. $('#vboxSnapshotList').children().empty().remove();
  430. // Fill snapshots if this tab is being shown
  431. if($('#vboxTabVMSnapshots').data('vboxShowing')) {
  432. // Keep track of last VM shown
  433. $('#vboxTabVMSnapshots').data('lastVM',vm.id);
  434. // append spinner
  435. $('#vboxSnapshotList').append($('<li />').attr({'class':'last'}).html("<div><img src='images/spinner.gif'></div>"));
  436. $.when(vboxAjaxRequest('machineGetSnapshots',{'vm':vm.id})).done(__vboxTabSnapshotsFill);
  437. }
  438. // No single selected VM or it is host
  439. } else {
  440. // disable tab
  441. $('#vboxTabVMSnapshots').parent().trigger('disableTab', ['vboxTabVMSnapshots']);
  442. $('#vboxTabVMSnapshots').data('lastVM',0);
  443. }
  444. /**
  445. *
  446. * VBOX event list triggered
  447. *
  448. */
  449. }).on('vboxEvents',function(e,eventList) {
  450. var redrawCurrent = false;
  451. for(var i = 0; i < eventList.length; i++) {
  452. switch(eventList[i].eventType) {
  453. //////////////////////////
  454. //
  455. // Snapshot events
  456. //
  457. /////////////////////////
  458. case 'OnSnapshotTaken':
  459. case 'OnSnapshotDeleted':
  460. case 'OnSnapshotRestored':
  461. case 'OnSnapshotChanged':
  462. // Is this vm selected
  463. if(vboxChooser.getSingleSelectedId() == eventList[i].machineId) {
  464. // Update title
  465. $('#vboxTabVMSnapshotsTitle').html(trans('Snapshots','UIVMDesktop') +
  466. (eventList[i].enrichmentData && eventList[i].enrichmentData.snapshotCount ? trans(' (%1)','VBoxSnapshotsWgt').replace('%1',eventList[i].enrichmentData.snapshotCount):''));
  467. // Redraw snapshots if this is shown
  468. if($('#vboxTabVMSnapshots').data('lastVM') == eventList[i].machineId) {
  469. $('#vboxSnapshotList').children().empty().remove();
  470. // Append spinner
  471. $('#vboxSnapshotList').append($('<li />').attr({'class':'last'}).html("<div><img src='images/spinner.gif'></div>"));
  472. $.when(vboxAjaxRequest('machineGetSnapshots',{'vm':eventList[i].machineId})).done(__vboxTabSnapshotsFill);
  473. return;
  474. }
  475. }
  476. break;
  477. /////////////////////////
  478. //
  479. // Session or state change
  480. //
  481. ////////////////////////
  482. case 'OnSessionStateChanged':
  483. case 'OnMachineStateChanged':
  484. if($('#vboxTabVMSnapshots').data('lastVM') == eventList[i].machineId && vboxChooser.getSingleSelectedId() == eventList[i].machineId) {
  485. redrawCurrent = true;
  486. }
  487. break;
  488. }
  489. }
  490. // Redraw current snapshot
  491. if(redrawCurrent) {
  492. var vmid = vboxChooser.getSingleSelectedId();
  493. // Get current state and details data
  494. $.when(vboxVMDataMediator.getVMData(vmid), vboxVMDataMediator.getVMDetails(vmid)).done(function(vm, vmd) {
  495. if($('#vboxTabVMSnapshots').data('lastVM') != vm.id) return;
  496. var selected = $('#vboxTabVMSnapshots').find('li.vboxSnapshotCurrentState').children('div.vboxListItemSelected').length;
  497. $('#vboxTabVMSnapshots').find('li.vboxSnapshotCurrentState').replaceWith(__vboxTabSnapshotCurrent($.extend(true,{},vm,vmd)));
  498. if(selected) {
  499. $('#vboxSnapshotList').trigger('select',
  500. $('#vboxTabVMSnapshots').find('li.vboxSnapshotCurrentState').children('div.vboxListItem').addClass('vboxListItemSelected').parent());
  501. }
  502. });
  503. }
  504. });
  505. // Load snapshots on show
  506. $('#vboxTabVMSnapshots').on('show',function(e){
  507. $('#vboxTabVMSnapshots').data('vboxShowing', 1);
  508. var vm = vboxChooser.getSingleSelected();
  509. if(vm && vm.id) {
  510. if($('#vboxTabVMSnapshots').data('lastVM') == vm.id) return;
  511. $('#vboxTabVMSnapshots').data('lastVM', vm.id);
  512. } else {
  513. $('#vboxSnapshotList').children().remove();
  514. $('#vboxTabVMSnapshots').data('lastVM',0);
  515. vboxSnapshotToolbar.disable();
  516. return;
  517. }
  518. // Get snapshots
  519. // Append spinner
  520. $('#vboxSnapshotList').append($('<li />').attr({'class':'last'}).html("<div><img src='images/spinner.gif'></div>"));
  521. $.when(vboxAjaxRequest('machineGetSnapshots',{'vm':vm.id})).done(__vboxTabSnapshotsFill);
  522. }).on('hide',function(e) {
  523. $('#vboxTabVMSnapshots').data('vboxShowing', 0);
  524. });
  525. /*
  526. * Fill Snapshots
  527. */
  528. function __vboxTabSnapshotsFill(response) {
  529. var snapshotData = response.responseData;
  530. if(vboxSnapshotToolbar._timer) {
  531. window.clearTimeout(vboxSnapshotToolbar._timer);
  532. vboxSnapshotToolbar._timer = null;
  533. }
  534. if(!snapshotData) return;
  535. // Get current state and details data
  536. $.when(vboxVMDataMediator.getVMData(response.responseData.vm), vboxVMDataMediator.getVMDetails(response.responseData.vm)).done(function(vm, vmd) {
  537. if($('#vboxTabVMSnapshots').data('lastVM') != vm.id) return;
  538. var list = $('#vboxSnapshotList');
  539. $(list).children().remove();
  540. var vmc = $.extend(true, {}, vm, vmd);
  541. // Snapshots exist
  542. if(snapshotData.snapshot && snapshotData.snapshot.name) {
  543. // Traverse snapshots
  544. $(list).append(__vboxTabSnapshot(snapshotData.snapshot, snapshotData.currentSnapshotId));
  545. // Append current state to last snapshot
  546. if(snapshotData.currentSnapshotId) {
  547. // Has children
  548. if($('#'+snapshotData.currentSnapshotId).children('ul').first()[0]) {
  549. $('#'+snapshotData.currentSnapshotId).children('ul').last().append(__vboxTabSnapshotCurrent(vmc));
  550. } else {
  551. $('#'+snapshotData.currentSnapshotId).append($('<ul />').append(__vboxTabSnapshotCurrent(vmc)));
  552. };
  553. };
  554. // No snapshots. Append current state to list
  555. } else {
  556. $(list).append(__vboxTabSnapshotCurrent(vmc));
  557. }
  558. // Init vbox tree list
  559. $('#vboxSnapshotList').vbtree();
  560. vboxSnapshotToolbar.enable();
  561. var lastListItem = $(list).find('li.vboxSnapshotCurrentState').last();
  562. lastListItem.children().addClass('vboxListItemSelected');
  563. $('#vboxSnapshotList').trigger('select',lastListItem);
  564. __vboxTabSnapshotTimestamps();
  565. });
  566. }
  567. /* Snapshot list item */
  568. function __vboxTabSnapshot(s, currentId) {
  569. var li = $('<li />').attr({'id':s.id});
  570. $(li).data('vboxSnapshot',s);
  571. // Use timestamp
  572. var t = '';
  573. if(s.timeStampSplit['seconds'] == 0)
  574. s.timeStampSplit['seconds'] = 1;
  575. var ago = 0;
  576. var ts = 'seconds';
  577. for(var i in s.timeStampSplit) {
  578. var l = Math.floor(t / s.timeStampSplit[i]);
  579. if(l > 0) {
  580. ago = l;
  581. ts = i;
  582. break;
  583. }
  584. }
  585. switch(ts) {
  586. case 'days':
  587. ts = trans('%n day(s)','VBoxGlobal', ago).replace('%n', ago);
  588. break;
  589. case 'hours':
  590. ts = trans('%n hour(s)', 'VBoxGlobal', ago).replace('%n', ago);
  591. break;
  592. case 'minutes':
  593. ts = trans('%n minute(s)', 'VBoxGlobal', ago).replace('%n', ago);
  594. break;
  595. case 'seconds':
  596. ts = trans('%n second(s)', 'VBoxGlobal', ago).replace('%n', ago);
  597. break;
  598. }
  599. ts = trans(' (%1 ago)','VBoxSnapshotsWgt').replace('%1', ts);
  600. $(li).append(' ').append(
  601. $('<div />').attr({'class':'vboxListItem'})
  602. .html('<img src="images/vbox/snapshot_'+(s.online ? 'online' : 'offline')+'_16px.png" height="16" width="16" /> ' +
  603. $('<div />').text(s.name).html())
  604. .append($('<span />').attr({'class':'timestamp'}).data({'vboxTimestamp':s.timeStamp}).text(ts))
  605. // Context menu
  606. .contextMenu({
  607. menu: vboxSnapshotContextMenu.menuId(),
  608. clickthrough: true
  609. },vboxSnapshotContextMenu.menuClickCallback)
  610. // show details on dblclick
  611. .dblclick(vboxSnapshotButtons[4].click).disableSelection()
  612. // tool tip
  613. .tipped({'position':'mouse','delay':1500,'source':'<p><strong>'+$('<div />').text(s.name).html()+'</strong> ('+trans((s.online ? 'online)' : 'offline)'),'VBoxSnapshotsWgt')+'</p>'+
  614. '<p>'+ vboxDateTimeString(s.timeStamp, trans('Taken at %1','VBoxSnapshotsWgt'), trans('Taken on %1','VBoxSnapshotsWgt'))+'</p>' +
  615. (s.description ? '<hr />' + $('<div />').text(s.description).html() : '')})
  616. ).addClass(currentId == s.id ? 'vboxSnapshotCurrent' : '').children('div.vboxListItem').first().click(function(){
  617. $('#vboxSnapshotList').find('div.vboxListItemSelected').first().removeClass('vboxListItemSelected');
  618. $(this).addClass('vboxListItemSelected');
  619. $('#vboxSnapshotList').trigger('select',$(this).parent());
  620. });
  621. if(s.children.length) {
  622. var ul = $('<ul />');
  623. for(var i = 0; i < s.children.length; i++) {
  624. $(ul).append(__vboxTabSnapshot(s.children[i], currentId));
  625. }
  626. $(li).append(ul);
  627. }
  628. return li;
  629. }
  630. /* Current state list item */
  631. function __vboxTabSnapshotCurrent(vm) {
  632. return $('<li />').data('vboxSnapshot',{'state':'current','name':trans((vm.currentStateModified ? 'Current State (changed)' : 'Current State'),'VBoxSnapshotsWgt')}).html(' ')
  633. .addClass('last vboxSnapshotCurrent vboxSnapshotCurrentState')
  634. .append(
  635. $('<div />').attr({'class':'vboxListItem'}).html('<img src="images/vbox/'+vboxMachineStateIcon(vm.state)+'" height="16" width="16" /> ' + $('<div />').text(trans((vm.currentStateModified ? 'Current State (changed)' : 'Current State'),'VBoxSnapshotsWgt')).html())
  636. .contextMenu({
  637. menu: vboxSnapshotContextMenuCurrent.menuId(),
  638. clickthrough : true
  639. },vboxSnapshotContextMenuCurrent.menuClickCallback)
  640. .click(function(){
  641. $('#vboxSnapshotList').find('div.vboxListItemSelected').first().removeClass('vboxListItemSelected');
  642. $(this).addClass('vboxListItemSelected');
  643. $('#vboxSnapshotList').trigger('select',$(this).parent());
  644. })
  645. .tipped({'position':'mouse','delay':1500,'source':'<strong>'+
  646. trans((vm.currentStateModified ? 'Current State (changed)' : 'Current State'),'VBoxSnapshotsWgt') + '</strong><br />'+
  647. trans('%1 since %2','VBoxSnapshotsWgt').replace('%1',trans(vboxVMStates.convert(vm.state),'VBoxGlobal'))
  648. .replace('%2',vboxDateTimeString(vm.lastStateChange))
  649. + (vm.snapshotCount > 0 ? '<hr />' + (vm.currentStateModified ?
  650. trans('The current state differs from the state stored in the current snapshot','VBoxSnapshotsWgt')
  651. : trans('The current state is identical to the state stored in the current snapshot','VBoxSnapshotsWgt'))
  652. : '')
  653. })
  654. );
  655. }
  656. /* Update snapshot timestamps */
  657. function __vboxTabSnapshotTimestamps() {
  658. // Shorthand
  659. var timeSpans = vboxSnapshotToolbar._timeSpans;
  660. // Keep minimum timestamp
  661. var minTs = 60;
  662. var currentTime = new Date();
  663. currentTime = Math.floor(currentTime.getTime() / 1000);
  664. $('#vboxTabVMSnapshots').find('span.timestamp').each(function(){
  665. var sts = parseInt($(this).data('vboxTimestamp'));
  666. var t = Math.max(currentTime - sts, 1);
  667. minTs = Math.min(minTs,t);
  668. // Check for max age.
  669. if(Math.floor(t / 86400) > 30) {
  670. var sdate = new Date(sts * 1000);
  671. $(this).html(trans(' (%1)','VBoxSnapshotsWgt').replace('%1',sdate.toLocaleString()));
  672. return;
  673. }
  674. var ago = 0;
  675. var ts = 'seconds';
  676. for(var i in timeSpans) {
  677. var l = Math.floor(t / timeSpans[i]);
  678. if(l > 0) {
  679. ago = l;
  680. ts = i;
  681. break;
  682. }
  683. }
  684. switch(ts) {
  685. case 'days':
  686. ts = trans('%n day(s)', 'VBoxGlobal', ago).replace('%n', ago);
  687. break;
  688. case 'hours':
  689. ts = trans('%n hour(s)', 'VBoxGlobal', ago).replace('%n', ago);
  690. break;
  691. case 'minutes':
  692. ts = trans('%n minute(s)', 'VBoxGlobal', ago).replace('%n', ago);
  693. break;
  694. case 'seconds':
  695. ts = trans('%n second(s)', 'VBoxGlobal', ago).replace('%n', ago);
  696. break;
  697. }
  698. $(this).html(ts = trans(' (%1 ago)','VBoxSnapshotsWgt').replace('%1', ts));
  699. });
  700. var timerSet = (minTs >= 60 ? 60 : 10);
  701. vboxSnapshotToolbar._timer = window.setTimeout(__vboxTabSnapshotTimestamps,(timerSet * 1000));
  702. }
  703. </script>
  704. </div>