Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376
  1. /**
  2. * @fileOverview Common utilities
  3. * @author Ian Moore (imoore76 at yahoo dot com)
  4. * @version $Id: utils.js 599 2015-07-27 10:40:37Z imoore76 $
  5. * @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com)
  6. * - unless otherwise noted in fuction
  7. */
  8. /**
  9. *
  10. * Prevent ESC key from stopping background AJAX requests
  11. *
  12. */
  13. $(document).ready(function(){
  14. $(window).keydown(function(i){if(i.keyCode&&i.keyCode===27){
  15. i.preventDefault();
  16. try {
  17. var flash = RDPWebClient.getFlashById("FlashRDP");
  18. flash.keyboardSendScancodes('01');
  19. } catch (e) {
  20. //alert(e.message);
  21. }
  22. }});
  23. $(document).keydown(function(i){if(i.keyCode&&i.keyCode===27){
  24. i.preventDefault();
  25. try {
  26. var flash = RDPWebClient.getFlashById("FlashRDP");
  27. flash.keyboardSendScancodes('01');
  28. } catch (e) {
  29. //alert(e.message);
  30. }
  31. }});
  32. });
  33. /**
  34. * Traverse a tree and return matching nodes.
  35. * @param {Object} tree - tree to traverse
  36. * @param {String} prop - node property to match
  37. * @param {Mixed} val - value that node property must match
  38. * @param {Boolean} all - return all results rather than stopping at first matching node (optional)
  39. * @param {String} children - search node children in property named by this argument (optional)
  40. * @return all matched nodes | first matched node | null
  41. */
  42. function vboxTraverse(tree,prop,val,all,children) {
  43. var leafs = new Array();
  44. for(var a in tree) {
  45. if(tree[a][prop] == val) {
  46. if(!all) return tree[a];
  47. leafs[leafs.length] = tree[a];
  48. }
  49. if(children && tree[a][children] && tree[a][children].length) {
  50. var c = vboxTraverse(tree[a][children],prop,val,all,children);
  51. if(!all && c) { return c; }
  52. else if(c && c.length) {
  53. leafs = leafs.concat(c);
  54. }
  55. }
  56. }
  57. return (all ? leafs : null);
  58. }
  59. /**
  60. * Performs AJAX request, alert()'s returned errors
  61. *
  62. * @param {String} fn - AJAX function to call
  63. * @param {Object} params - params to pass to AJAX call
  64. * @return {Object} deferred promise
  65. */
  66. function vboxAjaxRequest(fn,params,config) {
  67. // Promise for data
  68. var def = $.Deferred();
  69. // Fatal error previously occurred
  70. if($('#vboxPane').data('vboxFatalError'))
  71. return def.reject();
  72. var data = {
  73. 'fn': fn,
  74. 'params': params ? params : null,
  75. 'persist': config && config.persist ? config.persist : null
  76. };
  77. $.when($.post(vboxEndpointConfig.api, JSON.stringify(data), undefined,"json")
  78. // Run on error
  79. .fail(function(d,etext,xlr,d2) {
  80. // Fatal error previously occurred
  81. if($('#vboxPane').data('vboxFatalError')) return null;
  82. if(etext != 'error') {
  83. // Halt on parse errors
  84. if(etext.search(/parse/i) > -1) {
  85. $('#vboxPane').data('vboxFatalError',1);
  86. }
  87. if(window.console && window.console.log)
  88. window.console.log(etext + ': '+ d.responseText);
  89. vboxAlert({'error':'Ajax error: ' + etext,'details':d.responseText},{'width':'400px'});
  90. } else {
  91. // Check for error HTTP status
  92. if(d && d.status && (String(d.status).substring(0,1) == '4' || String(d.status).substring(0,1) == '5')) {
  93. var err = {error:'<div align="center">HTTP error: ' + d.status + ' ' + d.statusText+"</div>",details:''};
  94. for(var i in d) {
  95. if(typeof(d[i]) == 'function' || typeof(d[i]) == 'object') continue;
  96. err.details += i + ': "' + d[i] + '"' + "\n";
  97. }
  98. phpVirtualBoxFailure(err);
  99. } else {
  100. phpVirtualBoxFailure('<div align="center">(General communication failure)');
  101. }
  102. }
  103. return null;
  104. // Filter out data and display error messages
  105. }).pipe(function(d){
  106. // Fatal error previously occurred
  107. if($('#vboxPane').data('vboxFatalError')) {
  108. return null;
  109. }
  110. // Append debug output to console
  111. if(d && d.messages && window.console && window.console.log) {
  112. for(var i = 0; i < d.messages.length; i++) {
  113. window.console.log(d.messages[i]);
  114. }
  115. }
  116. if(d.errors.length > 0) {
  117. for(var i = 0; i < d.errors.length; i++) {
  118. // Handle fatal and connection errors
  119. if(d.errors[i].fatal || d.errors[i].connection) {
  120. // Multiple Servers check
  121. if(d.errors[i].connection && $('#vboxPane').data('vboxConfig') ) {
  122. $('#vboxPane').data('vboxFatalError',1);
  123. $('#vboxPane').css({'display':'none'});
  124. s='';
  125. if($('#vboxPane').data('vboxConfig').servers && $('#vboxPane').data('vboxConfig').servers.length) {
  126. var servers = $('#vboxPane').data('vboxConfig').servers;
  127. for(var a = 0; a < servers.length; a++) {
  128. servers[a] = "<a href='?server="+servers[a].name+"'>"+$('<div />').html(servers[a].name).text()+"</a>";
  129. }
  130. s = '<div style="display: block">'+trans('Server List','phpVirtualBox')+': '+servers.join(', ')+'</div>';
  131. }
  132. if(s) vboxAlert(s);
  133. vboxAlert(d.errors[i],{'width':'400px'});
  134. vboxAlert('<p>'+trans('An error occurred communicating with your vboxwebsrv. No more requests will be sent by phpVirtualBox until the error is corrected and this page is refreshed. The details of this connection error should be displayed in a subsequent dialog box.','phpVirtualBox')+'</p>'+s,{'width':'50%'});
  135. // Ignore connection errors until we have config data unless this was a login attempt
  136. } else if(!d.errors[i].connection || fn == 'login') {
  137. // If we have config data, and the error is fatal, halt processing
  138. if(d.errors[i].fatal && $('#vboxPane').data('vboxConfig')) {
  139. $('#vboxPane').data('vboxFatalError',1);
  140. $('#vboxPane').css({'display':'none'});
  141. }
  142. vboxAlert(d.errors[i],{'width':'400px'});
  143. }
  144. } else {
  145. // Error from normal request
  146. vboxAlert(d.errors[i],{'width':'400px'});
  147. }
  148. } // </ foreach error >
  149. } // </ if errors.length >
  150. return (d && d.data ? d.data : null);
  151. })
  152. ).done(function(d) {
  153. if(d) def.resolve(d);
  154. else def.reject();
  155. }).fail(function(){
  156. def.reject();
  157. });
  158. return def.promise();
  159. }
  160. /**
  161. * Return VRDE host address of VM
  162. * @param {Object} vm - virtual machine object
  163. * @return {String} VRDE host for VM
  164. */
  165. function vboxGetVRDEHost(vm) {
  166. var chost = ($('#vboxPane').data('vboxConfig').consoleHost ? $('#vboxPane').data('vboxConfig').consoleHost : (vm && vm.VRDEServer && vm.VRDEServer.netAddress ? vm.VRDEServer.netAddress : null));
  167. if(!chost) {
  168. // Set to host
  169. chost = $('#vboxPane').data('vboxConfig').host;
  170. // Check for localhost / 127.0.0.1
  171. if(!chost || chost == 'localhost' || chost == '127.0.0.1')
  172. chost = location.hostname;
  173. }
  174. return chost;
  175. }
  176. /**
  177. * Return the correct icon string relative to images/vbox/ for the guest OS type
  178. * @param {String} osTypeId - guest OS type id
  179. * @return {String} icon file name
  180. */
  181. function vboxGuestOSTypeIcon(osTypeId) {
  182. var strIcon = "os_other.png";
  183. switch (osTypeId)
  184. {
  185. case "Other": strIcon = "os_other.png"; break;
  186. case "DOS": strIcon = "os_dos.png"; break;
  187. case "Netware": strIcon = "os_netware.png"; break;
  188. case "L4": strIcon = "os_l4.png"; break;
  189. case "Windows31": strIcon = "os_win31.png"; break;
  190. case "Windows95": strIcon = "os_win95.png"; break;
  191. case "Windows98": strIcon = "os_win98.png"; break;
  192. case "WindowsMe": strIcon = "os_winme.png"; break;
  193. case "WindowsNT4": strIcon = "os_winnt4.png"; break;
  194. case "Windows2000": strIcon = "os_win2k.png"; break;
  195. case "WindowsXP": strIcon = "os_winxp.png"; break;
  196. case "WindowsXP_64": strIcon = "os_winxp_64.png"; break;
  197. case "Windows2003": strIcon = "os_win2k3.png"; break;
  198. case "Windows2003_64": strIcon = "os_win2k3_64.png"; break;
  199. case "WindowsVista": strIcon = "os_winvista.png"; break;
  200. case "WindowsVista_64": strIcon = "os_winvista_64.png"; break;
  201. case "Windows2008": strIcon = "os_win2k8.png"; break;
  202. case "Windows2008_64": strIcon = "os_win2k8_64.png"; break;
  203. case "Windows7": strIcon = "os_win7.png"; break;
  204. case "Windows7_64": strIcon = "os_win7_64.png"; break;
  205. case "Windows8": strIcon = "os_win8.png"; break;
  206. case "Windows8_64": strIcon = "os_win8_64.png"; break;
  207. case "Windows81": strIcon = "os_win81.png"; break;
  208. case "Windows81_64": strIcon = "os_win81_64.png"; break;
  209. case "Windows10": strIcon = "os_win10.png"; break
  210. case "Windows10_64": strIcon = "os_win10.png"; break
  211. case "WindowsNT": strIcon = "os_win_other.png"; break;
  212. case "WindowsNT_64": strIcon = "os_win_other_64.png"; break;
  213. case "Windows2012_64": strIcon = "os_win2k12_64.png"; break;
  214. case "Windows2016_64": strIcon = "os_win2k16_64.png"; break;
  215. case "OS2Warp3": strIcon = "os_os2warp3.png"; break;
  216. case "OS2Warp4": strIcon = "os_os2warp4.png"; break;
  217. case "OS2Warp45": strIcon = "os_os2warp45.png"; break;
  218. case "OS2eCS": strIcon = "os_os2ecs.png"; break;
  219. case "OS2": strIcon = "os_os2_other.png"; break;
  220. case "Linux_64": strIcon = "os_linux_64.png"; break;
  221. case "Linux": strIcon = "os_linux.png"; break;
  222. case "Linux22": strIcon = "os_linux22.png"; break;
  223. case "Linux24": strIcon = "os_linux24.png"; break;
  224. case "Linux24_64": strIcon = "os_linux24_64.png"; break;
  225. case "Linux26": strIcon = "os_linux26.png"; break;
  226. case "Linux26_64": strIcon = "os_linux26_64.png"; break;
  227. case "ArchLinux": strIcon = "os_archlinux.png"; break;
  228. case "ArchLinux_64": strIcon = "os_archlinux_64.png"; break;
  229. case "Debian": strIcon = "os_debian.png"; break;
  230. case "Debian_64": strIcon = "os_debian_64.png"; break;
  231. case "OpenSUSE": strIcon = "os_opensuse.png"; break;
  232. case "OpenSUSE_64": strIcon = "os_opensuse_64.png"; break;
  233. case "Fedora": strIcon = "os_fedora.png"; break;
  234. case "Fedora_64": strIcon = "os_fedora_64.png"; break;
  235. case "Gentoo": strIcon = "os_gentoo.png"; break;
  236. case "Gentoo_64": strIcon = "os_gentoo_64.png"; break;
  237. case "Mandriva": strIcon = "os_mandriva.png"; break;
  238. case "Mandriva_64": strIcon = "os_mandriva_64.png"; break;
  239. case "RedHat": strIcon = "os_redhat.png"; break;
  240. case "RedHat_64": strIcon = "os_redhat_64.png"; break;
  241. case "Turbolinux": strIcon = "os_turbolinux.png"; break;
  242. case "Turbolinux_64": strIcon = "os_turbolinux_64.png"; break;
  243. case "Ubuntu": strIcon = "os_ubuntu.png"; break;
  244. case "Ubuntu_64": strIcon = "os_ubuntu_64.png"; break;
  245. case "Xandros": strIcon = "os_xandros.png"; break;
  246. case "Xandros_64": strIcon = "os_xandros_64.png"; break;
  247. case "FreeBSD": strIcon = "os_freebsd.png"; break;
  248. case "FreeBSD_64": strIcon = "os_freebsd_64.png"; break;
  249. case "OpenBSD": strIcon = "os_openbsd.png"; break;
  250. case "OpenBSD_64": strIcon = "os_openbsd_64.png"; break;
  251. case "NetBSD": strIcon = "os_netbsd.png"; break;
  252. case "NetBSD_64": strIcon = "os_netbsd_64.png"; break;
  253. case "Solaris": strIcon = "os_solaris.png"; break;
  254. case "Solaris_64": strIcon = "os_solaris_64.png"; break;
  255. case "Solaris11_64": strIcon = "os_oraclesolaris_64.png"; break;
  256. case "OpenSolaris": strIcon = "os_oraclesolaris.png"; break;
  257. case "OpenSolaris_64": strIcon = "os_oraclesolaris_64.png"; break;
  258. case "QNX": strIcon = "os_qnx.png"; break;
  259. case "MacOS106": strIcon = "os_macosx.png"; break;
  260. case 'MacOS': strIcon = "os_macosx.png"; break;
  261. case "MacOS106_64": strIcon = "os_macosx_64.png"; break;
  262. case 'MacOS_64': strIcon = "os_macosx_64.png"; break;
  263. case 'Oracle': strIcon = "os_oracle.png"; break;
  264. case 'Oracle_64': strIcon = "os_oracle_64.png"; break;
  265. case 'JRockitVE': strIcon = 'os_jrockitve.png'; break;
  266. case "VirtualBox_Host": strIcon = "os_virtualbox.png"; break;
  267. default:
  268. break;
  269. }
  270. return strIcon;
  271. }
  272. /**
  273. * Return the correct icon relative to images/vbox/ for the VM state.
  274. * @param {String} state - virtual machine state
  275. * @return {String} icon file name
  276. */
  277. function vboxMachineStateIcon(state)
  278. {
  279. var strIcon = "state_powered_off_16px.png";
  280. var strNoIcon = "state_running_16px.png";
  281. switch (state)
  282. {
  283. case "PoweredOff": strIcon = "state_powered_off_16px.png"; break;
  284. case "Saved": strIcon = "state_saved_16px.png"; break;
  285. case "Saving": strIcon = "state_saving_16px.png"; break;
  286. case "Snapshotting": strIcon = "snapshot_offline_16px.png"; break;
  287. case "LiveSnapshotting": strIcon = "snapshot_online_16px.png"; break;
  288. case "Aborted": strIcon = "state_aborted_16px.png"; break;
  289. case "Running": strIcon = "state_running_16px.png"; break;
  290. case "Paused": strIcon = "state_paused_16px.png"; break;
  291. case "Stuck": strIcon = "state_stuck_16px.png"; break;
  292. case "Saving": strIcon = "state_discarding_16px.png"; break;
  293. case "Restoring": strIcon = "vm_settings_16px.png"; break;
  294. case "RestoringSnapshot": strIcon = "discard_cur_state_16px.png"; break;
  295. case "DeletingSnapshot": strIcon = "state_discarding_16px.png"; break;
  296. case "Hosting" : strIcon = "vm_settings_16px.png"; break;
  297. case "Inaccessible": strIcon = "state_aborted_16px.png"; break;
  298. default:
  299. strIcon = strNoIcon;
  300. }
  301. return strIcon;
  302. }
  303. /**
  304. * File or Folder browser dialog
  305. * @param {String} root - path to initial folder or file
  306. * @param {Function} fn - callback function to run when OK is clicked on dialog
  307. * @param {Boolean} foldersonly - only display / allow selection of folders (optional)
  308. * @param {String} title - title of dialog (optional)
  309. * @param {String} icon - URL to icon (optional)
  310. * @param {Boolean} strictFiles - only allow the OK button to be clicked when a file is selected (optional)
  311. */
  312. function vboxFileBrowser(root,fn,foldersonly,title,icon,strictFiles) {
  313. var buttons = { };
  314. buttons[trans('OK','QIMessageBox')] = function(f) {
  315. if(strictFiles && $('#vboxBrowseFolderList').find('.vboxListItemSelected').first().parent().hasClass('directory')) {
  316. $('#vboxBrowseFolderList').find('.vboxListItemSelected').first().trigger('dblclick');
  317. return;
  318. }
  319. if(typeof f != 'string') {
  320. f = $('#vboxBrowseFolderList').find('.vboxListItemSelected').first().attr('name');
  321. }
  322. $('#vboxBrowseFolder').trigger('close').empty().remove();
  323. fn(f);
  324. };
  325. buttons[trans('Cancel','QIMessageBox')] = function() { fn(null); $('#vboxBrowseFolder').trigger('close').empty().remove(); };
  326. var d1 = $('<div />').attr({'id':'vboxBrowseFolder','class':'vboxDialogContent','style':'display:none'});
  327. $('<div />').attr({'id':'vboxBrowseFolderList'}).fileTree({ 'root': (root ? root : '/'),'dirsOnly':foldersonly,'loadMessage':trans('Loading ...','UIVMDesktop'),'scrollTo':'#vboxBrowseFolder'},function(f){
  328. buttons[trans('OK','QIMessageBox')](f);
  329. }).appendTo(d1);
  330. $(d1).dialog({'closeOnEscape':true,'width':400,'height':600,'buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent','title':'<img src="'+(icon ? icon : 'images/jqueryFileTree/'+(foldersonly ? 'folder_open' : 'file')+'.png') + '" class="vboxDialogTitleIcon" /> ' + (title ? title : trans((foldersonly ? 'Select Folder' : 'Select File')))}).on("dialogbeforeclose",function(){
  331. $(this).parent().find('span:contains("'+trans('Cancel','QIMessageBox')+'")').trigger('click');
  332. });
  333. }
  334. /**
  335. * Convert megabytes to human readable string
  336. * @param {Integer} mb - megabytes
  337. * @return {String} human readable size representation (e.g. 2 GB, 500 MB, etc..)
  338. */
  339. function vboxMbytesConvert(mb) {return vboxBytesConvert(parseFloat(mb) * 1024 * 1024);}
  340. /**
  341. * Convert bytes to human readable string
  342. * @param {Integer} bytes - bytes
  343. * @return {String} human readable size representation (e.g. 2 GB, 500 MB, etc..)
  344. */
  345. function vboxBytesConvert(bytes) {
  346. var ext = new Array('B','KB','MB','GB','TB');
  347. var unitCount;
  348. for(unitCount=0; bytes >= 1024 && unitCount < ext.length; unitCount++) bytes = parseFloat(parseFloat(bytes)/1024);
  349. return Math.round(parseFloat(bytes)*Math.pow(10,2))/Math.pow(10,2) + " " + trans(ext[unitCount], 'VBoxGlobal');
  350. }
  351. /**
  352. * Parse str param into megabytes
  353. * @param {String} str - size string (2 TB, 500 MB, etc..) to parse
  354. * @return {Integer} megabytes
  355. */
  356. function vboxConvertMbytes(str) {
  357. str = str.replace(' ',' ');
  358. str = str.split(' ',2);
  359. if(!str[1]) str[1] = trans('MB','VBoxGlobal');
  360. var ext = new Array(trans('B','VBoxGlobal'),trans('KB','VBoxGlobal'),trans('MB','VBoxGlobal'),trans('GB','VBoxGlobal'),trans('TB','VBoxGlobal'));
  361. var index = jQuery.inArray(str[1],ext);
  362. if(index == -1) index = 2;
  363. switch(index) {
  364. case 0:
  365. return ((str[0] / 1024) / 1024);
  366. break;
  367. case 1:
  368. return (str[0] / 1024);
  369. break;
  370. case 3:
  371. return (str[0] * 1024);
  372. break;
  373. case 4:
  374. return (str[0] * 1024 * 1024);
  375. break;
  376. default:
  377. return (str[0]);
  378. }
  379. }
  380. /**
  381. * Display alert Dialog
  382. * @param {String|Object} e - message to display or object containing error message and details
  383. * @param {Object} xtraOpts - extra options to apply to alert jquery dialog (optional)
  384. * @see jQuery.dialog()
  385. */
  386. function vboxAlert(e,xtraOpts) {
  387. var acknowledged = $.Deferred();
  388. var msg = '';
  389. if(typeof e == 'object') msg = e.error;
  390. else msg = e;
  391. // Convert to <p>
  392. if(msg[0] != '<') msg = '<p>'+msg+'</p>';
  393. var div = $('<div />').attr({'class':'vboxDialogContent vboxAlert'}).html('<img src="images/50px-Warning_icon.svg.png" style="float: left; padding: 10px; height: 50px; width: 50px;" height="50" width="50" />'+msg);
  394. if(typeof e == 'object' && e.details) {
  395. // Details can contain HTML entities
  396. e.details = $('<div />').html(e.details).text();
  397. var p = $('<p />').attr({'style':'text-align: center'});
  398. $('<a />').attr({'href':'#'}).html(trans('Details','QIMessageBox')).click(function(){
  399. $(this).parent().parent().dialog('option',{'height':400,'position':'center'});
  400. $(this).parent().siblings(".vboxAlert").css({"display":""});
  401. $(this).parent().css({'padding':'0px','margin':'0px'});
  402. $(this).parent().siblings(".vboxAlert").siblings().empty().remove();
  403. return false;
  404. }).appendTo(p);
  405. $(div).append(p);
  406. var ddet = $('<div />').attr({'style':'display: none; height: 100%; width: auto;','class':'vboxAlert'});
  407. $('<textarea />').attr({'spellcheck':'false','wrap':'off','readonly':'true'}).val(e.details).appendTo($('<form />').appendTo(ddet));
  408. $(div).append(ddet);
  409. }
  410. var buttons = { };
  411. buttons[trans('OK','QIMessageBox')] = function(f) {
  412. $(this).trigger('close').empty().remove();
  413. acknowledged.resolve();
  414. };
  415. var dialogOpts = {'closeOnEscape':false,'width':600,'height':'auto','buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent','title':'<img src="images/vbox/OSE/about_16px.png" class="vboxDialogTitleIcon" /> phpVirtualBox'};
  416. if(typeof xtraOpts == "object") {
  417. for(var i in xtraOpts) {
  418. dialogOpts[i] = xtraOpts[i];
  419. }
  420. }
  421. $(div).dialog(dialogOpts);
  422. return acknowledged;
  423. }
  424. /**
  425. * Confirmation dialog
  426. * @param {String} q - question to ask
  427. * @param {Object} buttons -buttons to display on confirmation dialog
  428. * @param {String} cancelText - string displayed on Cancel button. Defaults to 'Cancel'
  429. * @param {Function} onCancel - function to run onCancel
  430. * @return {HTMLNode}
  431. * @see jQuery.dialog()
  432. */
  433. function vboxConfirm(q,buttons,cancelText,onCancel) {
  434. var div = $('<div />').attr({'class':'vboxDialogContent','style':'display: none; width: 500px;'}).html('<img src="images/50px-Question_icon.svg.png" style="height: 50px; width: 50px; float: left; padding: 10px;" height="50" width="50" />'+q);
  435. if(!cancelText) cancelText = trans('Cancel','QIMessageBox');
  436. buttons[cancelText] = function() { $(this).remove(); if(onCancel) { onCancel(); }};
  437. $(div).dialog({'closeOnEscape':false,'width':500,'height':'auto','buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent','title':'<img src="images/vbox/OSE/about_16px.png" class="vboxDialogTitleIcon" /> phpVirtualBox'});
  438. return $(div);
  439. }
  440. /**
  441. * Initialize common UI items
  442. * @param {String|HTMLNode} root - root HTML Node or node ID to initialize
  443. * @param {String} context - language context to use for translations
  444. * @see trans()
  445. */
  446. function vboxInitDisplay(root,context) {
  447. if(typeof root == 'string')
  448. root = $('#'+root);
  449. /*
  450. * Sliders
  451. */
  452. $(root).find('div.slider').each(function(){
  453. if($(this).hasClass('translateglob')) {
  454. $(this).closest('table').find(".translate").html(function(i,h){return trans($('<div />').html(h).text(),'VBoxGlobal');}).removeClass('translate');
  455. }
  456. var frm = $(this).data('form');
  457. if($(this).data('display')) {
  458. var fn = $(this).data('display');
  459. $(this).slider('option','slide',function(event,ui){
  460. document.forms[frm].elements[event.target.id + 'Value'].value = fn(ui.value);
  461. }).slider('option','change',function(event,ui){
  462. document.forms[frm].elements[event.target.id + 'Value'].value = fn(ui.value);
  463. });
  464. } else {
  465. $(this).slider('option','slide',function(event,ui){
  466. document.forms[frm].elements[event.target.id + 'Value'].value = ui.value;
  467. }).slider('option','change',function(event,ui){
  468. document.forms[frm].elements[event.target.id + 'Value'].value = ui.value;
  469. });
  470. }
  471. // Slider scale (ticks)
  472. $(this).children("div.sliderScale").each(function(){
  473. var min = $(this).parent().slider('option','min');
  474. var max = $(this).parent().slider('option','max');
  475. var diff = Math.min((max - min),50);
  476. var tdw = Math.round(100 / diff);
  477. var tr = $('<tr />');
  478. for(var a = 0; a < diff; a++) {
  479. $(tr).append($('<td />').attr({'style':'width: '+ tdw + '%'}));
  480. }
  481. $('<table />').attr({'class':'sliderScale'}).append(tr).appendTo(this);
  482. });
  483. // save value
  484. $(this).slider('value',$(this).slider('value'));
  485. // Min / Max labels
  486. if(!$(this).data('noMinMaxLabels')) {
  487. var min = $(this).slider('option','min');
  488. var max = $(this).slider('option','max');
  489. $(this).closest('table').find('.vboxSliderMin').html(function(i,h){return ' ' + trans(h,context,min,$(this).attr('title')).replace('%1',min);});
  490. $(this).closest('table').find('.vboxSliderMax').html(function(i,h){return ' ' + trans(h,context,max,$(this).attr('title')).replace('%1',max);});
  491. }
  492. });
  493. /*
  494. * Translations
  495. */
  496. $(root).find(".translate").html(function(i,h){return trans($('<div />').html(h).text(),context);}).removeClass('translate');
  497. /*
  498. * Setup Tabs
  499. */
  500. $(root).find(".vboxTabbed").tabs();
  501. /* Image buttons */
  502. if(!jQuery.browser.msie) {
  503. $(root).find('input.vboxImgButton').on('mousedown',function(){
  504. var xy = $(this).css('backgroundPosition').split(' ');
  505. if(!$(this).data('startX')) $(this).data('startX', parseInt(xy[0]));
  506. if(!$(this).data('startY')) $(this).data('startY', parseInt(xy[1]));
  507. $(this).css('backgroundPosition',(parseInt($(this).data('startX'))+1)+'px '+(parseInt($(this).data('startY'))+1)+'px');
  508. var btn = this;
  509. $(document).one('mouseup',function(){
  510. $(btn).css('backgroundPosition',$(btn).data('startX')+'px '+$(btn).data('startY')+'px');
  511. });
  512. });
  513. }
  514. /*
  515. *
  516. * Enable / disable sections (Remote Display, Audio, Network Adapters, usb)
  517. *
  518. */
  519. $(root).find('input.vboxEnablerCheckbox').on('click', function(e) {
  520. var roottbl = $(this).closest('table');
  521. $(roottbl).find('input:not(.vboxEnablerCheckbox)').prop('disabled',!this.checked);
  522. $(roottbl).find('select:not(.vboxEnablerIgnore)').prop('disabled',!this.checked);
  523. (this.checked ? $(roottbl).find('th').removeClass('vboxDisabled') : $(roottbl).find('th:not(.vboxEnablerIgnore)').addClass('vboxDisabled'));
  524. (this.checked ? $(roottbl).find('.vboxEnablerListen').removeClass('vboxDisabled') : $(roottbl).find('.vboxEnablerListen').addClass('vboxDisabled'));
  525. // Find any enabler / disabler listeners
  526. $(roottbl).find('.vboxEnablerTrigger').trigger(this.checked ? 'enable' : 'disable');
  527. });
  528. /*
  529. * Tooltips
  530. */
  531. $(root).find('.vboxToolbarSmallButton').tipped({'source':'title','mode':'hover'});
  532. /*
  533. * File / Folder browsers
  534. */
  535. if($('#vboxPane').data('vboxConfig').browserDisable) {
  536. $(root).find('table td.vboxFileFolderInput input.vboxImgButton').hide();
  537. }
  538. }
  539. /**
  540. * Color VISIBLE children rows of parent elm
  541. * @param {HTMLNode} elm - element who's children to color
  542. * @param {Boolean} startOdd - start on the 2nd child (optional)
  543. * @param {String} headerClass - if child node has headerClass class, consider it a header and skip coloring (optional)
  544. */
  545. function vboxColorRows(elm,startOdd,headerClass) {
  546. var odd = 0;
  547. if(startOdd) odd = 1;
  548. $(elm).children().each(function(i){
  549. if(headerClass && $(this).hasClass(headerClass)) {
  550. odd = (startOdd ? 1 : 0);
  551. return;
  552. }
  553. if($(this).css('display') == 'none' || $(this).hasClass('vboxListItemDisabled')) return;
  554. (odd++ % 2 ? $(this).addClass('vboxOddRow') : $(this).removeClass('vboxOddRow'));
  555. });
  556. }
  557. /**
  558. * Return an HTML div node sized to parent with overflow hidden
  559. * @param {HTMLNode} p - node to add div to
  560. * @return {HTMLNode}
  561. */
  562. function vboxDivOverflowHidden(p) {
  563. var w = $(p).innerWidth();
  564. w -= parseInt($(p).css('padding-right'));
  565. w -= parseInt($(p).css('padding-left'));
  566. return $('<div />').css({'width':(w-4)+'px','overflow':'hidden','padding':'0px','margin':'0px','border':'0px'});
  567. }
  568. /**
  569. * Show progress dialog and periodically poll the progress' status
  570. *
  571. * @param {String} prequest - request object passed to ajax
  572. * @param {Function} callback - function to run on progress completion
  573. * @param {String} icon - URL of image to display on progress operation dialog (optional)
  574. * @param {String} title - title of progress operation dialog (optional)
  575. * @param {String} target - contextual target of progress operation
  576. * @param {Boolean} blocking - true if progress operation should block other ops
  577. * @see vboxconnector::progressGet()
  578. */
  579. function vboxProgress(prequest,callback,icon,title,target,blocking) {
  580. // Fix title
  581. title = title.replace('\.+$','');
  582. // Sanitize target
  583. target = $('<div />').text(target).html();
  584. // Sanitize progress request data
  585. var persist = prequest.persist;
  586. prequest = {
  587. 'progress' : prequest.progress,
  588. 'catcherrs' : prequest.catcherrs
  589. };
  590. // Blocking creates a dialog
  591. if(!blocking) {
  592. vboxProgressCreateListElement(prequest,icon,title,target,callback);
  593. $.when(prequest, vboxAjaxRequest('progressGet',prequest,{'persist':persist})).done(vboxProgressUpdate);
  594. } else {
  595. vboxProgressCreateDialog(prequest,icon,title,target,callback);
  596. $.when(prequest, vboxAjaxRequest('progressGet',prequest,{'persist':persist})).done(vboxProgressUpdateModal);
  597. }
  598. }
  599. /**
  600. * Generate modal progress dialog
  601. *
  602. * @param {Object} prequest - progress operation request object
  603. * @param {String} icon - URL of image to display on progress operation dialog (optional)
  604. * @param {String} title - title of progress operation dialog (optional)
  605. * @param {String} target - contextual target of progress operation
  606. * @param {Function} callback - function to run on progress completion
  607. * @see vboxconnector::progressGet()
  608. */
  609. function vboxProgressCreateDialog(prequest,icon,title,target,callback) {
  610. // Shorthand
  611. var pid = prequest.progress;
  612. var div = $('<div />').attr({'id':'vboxProgress'+pid,'title':(title ? title : 'phpVirtualBox'),'style':'text-align: center'});
  613. var tbl = $('<table />').css({'width':'100%'});
  614. var tr = $('<tr />').css({'vertical-align':'middle'});
  615. var td = $('<td />').css({'padding':'0px','text-align':'left','width':'1px'});
  616. if(icon) {
  617. $('<img />').css({'margin':'4px'}).attr({'src':'images/vbox/'+icon,'height':'90','width':'90'}).appendTo(td);
  618. }
  619. $(tr).append(td);
  620. var td = $('<td />').css({'text-align':'center','padding':'4px'}).append($('<div />').attr({'id':'vboxProgressBar'+pid,'margin':'4px'}).progressbar({ value: 1 }));
  621. $('<div />').attr({'id':'vboxProgressText'+pid}).html('<img src="images/spinner.gif" />').appendTo(td);
  622. // Cancel button
  623. $('<div />').attr({'id':'vboxProgressCancel'+pid}).css({'display':'none','padding':'8px'}).append(
  624. $('<input />').attr('type','button').val(trans('Cancel','QIMessageBox')).data({'pid':pid}).click(function(){
  625. this.disabled = 'disabled';
  626. vboxAjaxRequest('progressCancel',prequest);
  627. })
  628. ).appendTo(td);
  629. $(tbl).append($(tr).append(td)).appendTo(div);
  630. // Append placeholder for list element
  631. $('#vboxProgressOps').prepend($('<div />').addClass('vboxProgressOpElement').css({'display':'none'}).attr({'id':'vboxProgressPlaceholder'+pid}));
  632. $(div).data({
  633. 'vboxCallback':callback,
  634. 'vboxIcon' : icon,
  635. 'vboxTitle' : title,
  636. 'vboxTarget' : target
  637. }).dialog({'width':400,'height':'auto','closeOnEscape':false,'modal':true,'resizable':false,'draggable':true,'closeOnEscape':false,'buttons':{}});
  638. }
  639. /**
  640. * Generate progress list element and append it
  641. *
  642. * @param {Object} prequest - progress operation request object
  643. * @param {String} icon - URL of image to display on progress operation dialog (optional)
  644. * @param {String} title - title of progress operation dialog (optional)
  645. * @param {String} target - contextual target of progress operation
  646. * @param {Function} callback - function to run on progress completion
  647. * @see vboxconnector::progressGet()
  648. */
  649. function vboxProgressCreateListElement(prequest,icon,title,target,callback) {
  650. // Shorthand
  651. var pid = prequest.progress;
  652. var div = $('<div />').attr({'id':'vboxProgress'+pid}).addClass('vboxProgressOpElement');
  653. var divOpTitle = $('<div />').addClass('vboxProgressOpTitle');
  654. if(icon) {
  655. $('<img />').attr({'src':'images/vbox/'+icon,'height':'16','width':'16'}).appendTo(divOpTitle);
  656. }
  657. // Title
  658. if($('#vboxPane').data('vboxConfig').servers.length) {
  659. title = $('#vboxPane').data('vboxConfig').name + ': ' + title;
  660. }
  661. $(divOpTitle).append(title + (target ? ' (' + target + ')' : '')).appendTo(div);
  662. // Progress bar
  663. $('<div />').addClass('vboxProgressBarContainer').append(
  664. $('<div />').attr({'id':'vboxProgressBar'+pid}).progressbar({ value: 1 })
  665. ).appendTo(div);
  666. // Progress text
  667. $('<div />').addClass('vboxProgressOpText').append(
  668. $('<span />').attr({'id':'vboxProgressText'+pid}).html('<img src="images/spinner.gif" height=12 width=12/>')
  669. ).appendTo(div);
  670. // Cancel button
  671. $('<div />').addClass('vboxProgressOpCancel').append(
  672. $('<input />').attr({'id':'vboxProgressCancel'+pid,'type':'button'}).val(trans('Cancel','UIProgressDialog')).data({'pid':pid})
  673. .click(function(){
  674. this.disabled = 'disabled';
  675. vboxAjaxRequest('progressCancel',prequest);
  676. })
  677. .css({'margin':'0px'})
  678. ).appendTo(div);
  679. $(div).data({'vboxCallback':callback})
  680. if($('#vboxProgressPlaceholder'+pid)[0]) {
  681. $('#vboxProgressPlaceholder'+pid).replaceWith(div);
  682. } else {
  683. $('#vboxProgressOps').prepend(div);
  684. }
  685. }
  686. /**
  687. * OnUnload warning shown when an operation is in progress
  688. * @return {String} warning message indicating operation is in progress
  689. */
  690. function vboxOpInProgressCheck() {
  691. if($('#vboxProgressOps').children('div.vboxProgressOpElement:not(.vboxProgressComplete)').addClass('vboxProgressRunning').length) {
  692. return trans('Warning: A VirtualBox internal operation is in progress. Closing this window or navigating away from this web page may cause unexpected and undesirable results. Please wait for the operation to complete.','phpVirtualBox');
  693. }
  694. }
  695. /**
  696. * Update progress dialog box. Callback run from vboxAjaxRequest
  697. *
  698. * @param {Object} prequest - progress operation data passed to ajax call
  699. * @param {Object} data - data returned from progressGet AJAX call
  700. */
  701. function vboxProgressUpdateModal(prequest, data) {
  702. vboxProgressUpdate(prequest,data,true);
  703. }
  704. /**
  705. * Update progress dialog box or progress list row with % completed
  706. *
  707. * @param {Object} prequest - progress operation data passed to ajax call
  708. * @param {Object} d - data returned from progressGet AJAX call
  709. * @param {Boolean} modal - true if updating modal dialog
  710. * @see vboxconnector::progressGet()
  711. */
  712. function vboxProgressUpdate(prequest,d,modal) {
  713. // Shorthand
  714. var pid = prequest.progress;
  715. // check for completed progress
  716. if(!d || !d.responseData || !d.responseData['progress'] || !d.responseData['info'] || d.responseData['info']['completed'] || d.responseData['info']['canceled']) {
  717. if(d && d.responseData['info'] && d.responseData['info']['canceled'])
  718. vboxAlert(trans('Operation Canceled','phpVirtualBox'),{'width':'300px','height':'auto'});
  719. var callback = $("#vboxProgress"+pid).data('vboxCallback');
  720. $("#vboxProgressBar"+pid).progressbar({ value: 100 });
  721. if(modal) {
  722. var icon = $("#vboxProgress"+pid).data('vboxIcon');
  723. var title = $("#vboxProgress"+pid).data('vboxTitle');
  724. var target = $("#vboxProgress"+pid).data('vboxTarget');
  725. $("#vboxProgress"+pid).empty().remove();
  726. if(callback) callback(d);
  727. // Now append to list
  728. vboxProgressCreateListElement(prequest,icon,title,target);
  729. vboxProgressUpdate(prequest);
  730. } else {
  731. var sdate = new Date();
  732. $("#vboxProgressText"+pid).html(sdate.toLocaleString());
  733. $('#vboxProgressCancel'+pid).remove();
  734. if(callback) callback(d);
  735. }
  736. $("#vboxProgress"+pid).addClass('vboxProgressComplete').removeClass('vboxProgressRunning');
  737. // Remove data
  738. $("#vboxProgress"+pid).removeData([
  739. 'vboxCallback',
  740. 'vboxIcon',
  741. 'vboxTitle',
  742. 'vboxTarget'
  743. ]);
  744. // Check for max elements
  745. if($('#vboxPane').data('vboxConfig').maxProgressList) {
  746. var maxList = $('#vboxPane').data('vboxConfig').maxProgressList;
  747. try {
  748. maxList = Math.max(2,parseInt(maxList));
  749. } catch (e) {
  750. maxList = 5;
  751. }
  752. if(maxList > 0) $('#vboxProgressOps').children('div.vboxProgressComplete').slice(maxList).remove();
  753. }
  754. return;
  755. }
  756. // update percent
  757. $("#vboxProgressBar"+pid).progressbar({ value: d.responseData.info.percent });
  758. $("#vboxProgressText"+pid).html(d.responseData.info.percent+'%'+(modal ? '<br />' : ' ') + d.responseData.info.operationDescription);
  759. // Cancelable?
  760. if(d.responseData.info.cancelable) {
  761. $('#vboxProgressCancel'+pid).show();
  762. }
  763. // Get request
  764. var def = $.Deferred();
  765. def.done(function(){
  766. $.when(prequest, vboxAjaxRequest('progressGet', prequest, {'persist': d.persist}))
  767. .done((modal ? vboxProgressUpdateModal : vboxProgressUpdate));
  768. });
  769. window.setTimeout(def.resolve, 2000);
  770. }
  771. /**
  772. * Position element to mouse event
  773. * @param {HTMLNode} elm - HTML node to position
  774. * @param {Event} e - Event to position to
  775. */
  776. function vboxPositionEvent(elm,e) {
  777. var d = {};
  778. if( self.innerHeight ) {
  779. d.pageYOffset = self.pageYOffset;
  780. d.pageXOffset = self.pageXOffset;
  781. d.innerHeight = self.innerHeight;
  782. d.innerWidth = self.innerWidth;
  783. } else if( document.documentElement &&
  784. document.documentElement.clientHeight ) {
  785. d.pageYOffset = document.documentElement.scrollTop;
  786. d.pageXOffset = document.documentElement.scrollLeft;
  787. d.innerHeight = document.documentElement.clientHeight;
  788. d.innerWidth = document.documentElement.clientWidth;
  789. } else if( document.body ) {
  790. d.pageYOffset = document.body.scrollTop;
  791. d.pageXOffset = document.body.scrollLeft;
  792. d.innerHeight = document.body.clientHeight;
  793. d.innerWidth = document.body.clientWidth;
  794. }
  795. $(elm).css({'left':0,'top':0});
  796. (e.pageX) ? x = e.pageX : x = e.clientX + d.scrollLeft;
  797. (e.pageY) ? y = e.pageY : y = e.clientY + d.scrollTop;
  798. //adjust to ensure element is inside viewable screen
  799. var right = x + $(elm).outerWidth();
  800. var bottom = y + $(elm).outerHeight();
  801. var windowWidth = $(window).width() + $(window).scrollLeft()-5;
  802. var windowHeight = $(window).height() + $(window).scrollTop()-5;
  803. x = (right > windowWidth) ? x - (right - windowWidth) : x;
  804. y = (bottom > windowHeight) ? y - (bottom - windowHeight) : y;
  805. $(elm).css({ top: y, left: x });
  806. }
  807. /**
  808. * Position element inside visible window
  809. * @param {HTMLNode} elm - element
  810. */
  811. function vboxPositionToWindow(elm) {
  812. var offset = $(elm).offset();
  813. var x = offset.left;
  814. var y = offset.top;
  815. //adjust to ensure menu is inside viewable screen
  816. var right = x + $(elm).outerWidth();
  817. var bottom = y + $(elm).outerHeight();
  818. var windowWidth = $(window).width() + $(window).scrollLeft();
  819. var windowHeight = $(window).height() + $(window).scrollTop();
  820. x = (right > windowWidth) ? x - (right - windowWidth) : x;
  821. y = (bottom > windowHeight) ? y - (bottom - windowHeight) : y;
  822. $(elm).css({'top':y,'left':x});
  823. }
  824. /*
  825. * keycode input validation functions
  826. */
  827. /**
  828. * Return true if k param is a number
  829. * @param {Integer} k - keycode
  830. * @return {Boolean}
  831. */
  832. function vboxValidateNum(k) {
  833. return ((k >= 96 && k <= 105)||(k >= 48 && k <= 57));
  834. }
  835. /**
  836. * Return true if k param is a number or '.'
  837. * @param {Integer} k - keycode
  838. * @return {Boolean}
  839. */
  840. function vboxValidateIP(k) {
  841. return (vboxValidateNum(k) || k == 190 || k == 110 || k == 59 || k==78);
  842. }
  843. /**
  844. * Return true if k param is a valid control code (shift, backspace, etc..)
  845. * @param {Integer} k - keycode
  846. * @return {Boolean}
  847. */
  848. function vboxValidateCtrl(k) {
  849. switch(k) {
  850. case 8: // backspace
  851. case 37: // left | right
  852. case 39:
  853. case 27: // esc
  854. case 16: // shift
  855. case 17: // ctrl
  856. case 35: // end
  857. case 36: // home
  858. case 46: // del
  859. case 144: // numlock
  860. case 20: // capslock
  861. case 18: // alt
  862. return true;
  863. }
  864. return false;
  865. }
  866. /** Parse Cookies and populate $('#vboxPane').data('vboxCookies') */
  867. function vboxParseCookies() {
  868. if($('#vboxPane').data('vboxCookiesParsed')) return;
  869. var cookies = {};
  870. var c = document.cookie.split('; ');
  871. for(var i = 0; i < c.length; i++) {
  872. var nv = c[i].split('=');
  873. cookies[nv[0]] = nv[1];
  874. }
  875. $('#vboxPane').data('vboxCookies', cookies);
  876. $('#vboxPane').data('vboxCookiesParsed',true);
  877. }
  878. /**
  879. * General application failure
  880. * @param {String|Object} msg - Optional extra message appended to error
  881. * or error object passed to vboxAlert
  882. */
  883. function phpVirtualBoxFailure(msg) {
  884. if($('#vboxPane').data('vboxFatalError')) return;
  885. $('#vboxPane').data('vboxFatalError', 1);
  886. $('#vboxPane').css({'display':'none'});
  887. $('#vboxPane').trigger('phpVirtualBoxFailure');
  888. if(typeof(msg) == 'string') {
  889. vboxAlert(trans('There was an error obtaining the list of registered virtual machines from VirtualBox. Make sure vboxwebsrv is running and that the settings in config.php are correct.<p>The list of virtual machines will not begin auto-refreshing again until this page is reloaded.</p>','phpVirtualBox')+(msg ? msg : ''));
  890. } else {
  891. msg.error = trans('There was an error obtaining the list of registered virtual machines from VirtualBox. Make sure vboxwebsrv is running and that the settings in config.php are correct.<p>The list of virtual machines will not begin auto-refreshing again until this page is reloaded.</p>','phpVirtualBox') + msg.error;
  892. vboxAlert(msg);
  893. }
  894. }
  895. /**
  896. * Set a cookie and update $('#vboxPane').data('vboxCookies')
  897. * @param {String} k - cookie key
  898. * @param {any} v - cookie value
  899. * @param {Date} expire - when cookie should expire
  900. */
  901. function vboxSetCookie(k,v,expire) {
  902. var exp = (v ? (expire ? expire : new Date(2020,12,24)) : new Date().setDate(new Date().getDate() - 1));
  903. document.cookie = k+"="+v+"; expires="+exp.toGMTString()+"; path=/";
  904. $('#vboxPane').data('vboxCookies')[k] = v;
  905. }
  906. /**
  907. * Set a local data item using the local storage mechanism
  908. * and upate $('#vboxPane').data('vboxCookies');
  909. * @param {String} k - data item key
  910. * @param {any} v - data item value
  911. * @param {Boolean} nocookies - do not fall back to cookies
  912. */
  913. function vboxSetLocalDataItem(k,v,nocookies) {
  914. // fall back to normal cookie
  915. if(typeof(Storage)==="undefined") {
  916. if(!nocookies) vboxSetCookie(k,v);
  917. return;
  918. }
  919. // Remove item?
  920. if(v) {
  921. localStorage.setItem(k,v.toString());
  922. } else {
  923. localStorage.removeItem(k);
  924. }
  925. }
  926. /**
  927. * Get a local data item using the local storage mechanism
  928. * @param {String} k - data item key
  929. * @return {mixed} data item value
  930. */
  931. function vboxGetLocalDataItem(k) {
  932. // fall back to normal cookie
  933. if(typeof(Storage)==="undefined") {
  934. return $('#vboxPane').data('vboxCookies')[k];
  935. }
  936. return localStorage.getItem(k);
  937. }
  938. /**
  939. * Strip file name from path
  940. * @param {String} p - path
  941. * @return {String} path minus file name
  942. */
  943. function vboxDirname(p) {
  944. var pos = p.lastIndexOf($('#vboxPane').data('vboxConfig').DSEP);
  945. if(pos > -1) {
  946. return p.substring(0,pos);
  947. }
  948. return p;
  949. }
  950. /**
  951. * Strip dir name from path
  952. * @param {String} p - path
  953. * @return {String} file name portion of path
  954. */
  955. function vboxBasename(p) {
  956. var pos = p.lastIndexOf($('#vboxPane').data('vboxConfig').DSEP);
  957. if(pos > -1) {
  958. return p.substring((pos+1));
  959. }
  960. return p;
  961. }
  962. /**
  963. * Return a time or date+time string depending on
  964. * how much time has elapsed
  965. * @param {Integer} t - seconds since 1/1/1970 0:0:0
  966. * @param {String} replaceTime - optional string to return replacing time
  967. * @param {String} replaceDateTime - optional string to return replace date_time
  968. * @return {String} time or date+time string
  969. */
  970. function vboxDateTimeString(t, replaceTime, replaceDateTime) {
  971. var sdate = new Date(t*1000);
  972. if((new Date().getTime() - sdate.getTime())/1000 > 86400
  973. || new Date().getDate() != sdate.getDate()) {
  974. return (replaceDateTime ? replaceDateTime.replace('%1',sdate.toLocaleString()) : sdate.toLocaleString());
  975. }
  976. return (replaceTime ? replaceTime.replace('%1',sdate.toLocaleTimeString()) : sdate.toLocaleTimeString());
  977. }
  978. /**
  979. * Calculate scrollbar width
  980. * @return {Integer} width of scrollbar
  981. *
  982. * http://www.alexandre-gomes.com/?p=115
  983. *
  984. */
  985. var getScrollbarWidth = function() {
  986. var inner = document.createElement('p');
  987. inner.style.width = "100%";
  988. inner.style.height = "200px";
  989. var outer = document.createElement('div');
  990. outer.style.position = "absolute";
  991. outer.style.top = "0px";
  992. outer.style.left = "0px";
  993. outer.style.visibility = "hidden";
  994. outer.style.width = "200px";
  995. outer.style.height = "150px";
  996. outer.style.overflow = "hidden";
  997. outer.appendChild (inner);
  998. document.body.appendChild (outer);
  999. var w1 = inner.offsetWidth;
  1000. outer.style.overflow = 'scroll';
  1001. var w2 = inner.offsetWidth;
  1002. if (w1 == w2) w2 = outer.clientWidth;
  1003. document.body.removeChild (outer);
  1004. return (w1 - w2);
  1005. };
  1006. /**
  1007. * Returns the result of case-insensitive string comparison using 'natural' algorithm comparing str1 to str2
  1008. * @param {String} str1 - 1st string
  1009. * @param {String} str2 - 2nd string
  1010. * @return {Integer} integer for use in list sorting comparison
  1011. */
  1012. function strnatcasecmp(str1, str2) {
  1013. // Returns the result of case-insensitive string comparison using 'natural' algorithm
  1014. //
  1015. // version: 1004.2314
  1016. // discuss at: http://phpjs.org/functions/strnatcasecmp // + original by: Martin Pool
  1017. // + reimplemented by: Pierre-Luc Paour
  1018. // + reimplemented by: Kristof Coomans (SCK-CEN (Belgian Nucleair Research Centre))
  1019. // + reimplemented by: Brett Zamir (http://brett-zamir.me)
  1020. // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // * example 1: strnatcasecmp(10, 1);
  1021. // * returns 1: 1
  1022. // * example 1: strnatcasecmp('1', '10');
  1023. // * returns 1: -1
  1024. var a = (str1+'').toLowerCase(); var b = (str2+'').toLowerCase();
  1025. var isWhitespaceChar = function (a) {
  1026. return a.charCodeAt(0) <= 32;
  1027. };
  1028. var isDigitChar = function (a) {
  1029. var charCode = a.charCodeAt(0);
  1030. return ( charCode >= 48 && charCode <= 57 );
  1031. };
  1032. var compareRight = function (a,b) {
  1033. var bias = 0;
  1034. var ia = 0;
  1035. var ib = 0;
  1036. var ca;
  1037. var cb;
  1038. // The longest run of digits wins. That aside, the greatest // value wins, but we can't know that it will until we've scanned
  1039. // both numbers to know that they have the same magnitude, so we
  1040. // remember it in BIAS.
  1041. for (;; ia++, ib++) {
  1042. ca = a.charAt(ia); cb = b.charAt(ib);
  1043. if (!isDigitChar(ca) &&
  1044. !isDigitChar(cb)) {
  1045. return bias; } else if (!isDigitChar(ca)) {
  1046. return -1;
  1047. } else if (!isDigitChar(cb)) {
  1048. return +1;
  1049. } else if (ca < cb) { if (bias == 0) {
  1050. bias = -1;
  1051. }
  1052. } else if (ca > cb) {
  1053. if (bias == 0) { bias = +1;
  1054. }
  1055. } else if (ca == 0 && cb == 0) {
  1056. return bias;
  1057. } }
  1058. };
  1059. var ia = 0, ib = 0;
  1060. var nza = 0, nzb = 0; var ca, cb;
  1061. var result;
  1062. while (true) {
  1063. // only count the number of zeroes leading the last number compared nza = nzb = 0;
  1064. ca = a.charAt(ia);
  1065. cb = b.charAt(ib);
  1066. // skip over leading spaces or zeros
  1067. while (isWhitespaceChar( ca ) || ca =='0') {
  1068. if (ca == '0') {
  1069. nza++;
  1070. } else { // only count consecutive zeroes
  1071. nza = 0;
  1072. }
  1073. ca = a.charAt(++ia); }
  1074. while (isWhitespaceChar( cb ) || cb == '0') {
  1075. if (cb == '0') {
  1076. nzb++; } else {
  1077. // only count consecutive zeroes
  1078. nzb = 0;
  1079. }
  1080. cb = b.charAt(++ib);
  1081. }
  1082. // process run of digits
  1083. if (isDigitChar(ca) && isDigitChar(cb)) { if ((result = compareRight(a.substring(ia), b.substring(ib))) != 0) {
  1084. return result;
  1085. }
  1086. }
  1087. if (ca == 0 && cb == 0) {
  1088. // The strings compare the same. Perhaps the caller
  1089. // will want to call strcmp to break the tie.
  1090. return nza - nzb;
  1091. }
  1092. if (ca < cb) {
  1093. return -1;
  1094. } else if (ca > cb) {
  1095. return +1; }
  1096. ++ia; ++ib;
  1097. }
  1098. }
  1099. /** Filter prototype for older browsers
  1100. * https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter
  1101. */
  1102. if (!Array.prototype.filter)
  1103. {
  1104. Array.prototype.filter = function(fun /*, thisp */)
  1105. {
  1106. "use strict";
  1107. if (this == null)
  1108. throw new TypeError();
  1109. var t = Object(this);
  1110. var len = t.length >>> 0;
  1111. if (typeof fun != "function")
  1112. throw new TypeError();
  1113. var res = [];
  1114. var thisp = arguments[1];
  1115. for (var i = 0; i < len; i++)
  1116. {
  1117. if (i in t)
  1118. {
  1119. var val = t[i]; // in case fun mutates this
  1120. if (fun.call(thisp, val, i, t))
  1121. res.push(val);
  1122. }
  1123. }
  1124. return res;
  1125. };
  1126. }
  1127. $(document).ready(function() {
  1128. // Don't unload while progress operation is .. in progress
  1129. window.onbeforeunload = vboxOpInProgressCheck;
  1130. });