/** * @fileOverview Common utilities * @author Ian Moore (imoore76 at yahoo dot com) * @version $Id: utils.js 599 2015-07-27 10:40:37Z imoore76 $ * @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com) * - unless otherwise noted in fuction */ /** * * Prevent ESC key from stopping background AJAX requests * */ $(document).ready(function(){ $(window).keydown(function(i){if(i.keyCode&&i.keyCode===27){ i.preventDefault(); try { var flash = RDPWebClient.getFlashById("FlashRDP"); flash.keyboardSendScancodes('01'); } catch (e) { //alert(e.message); } }}); $(document).keydown(function(i){if(i.keyCode&&i.keyCode===27){ i.preventDefault(); try { var flash = RDPWebClient.getFlashById("FlashRDP"); flash.keyboardSendScancodes('01'); } catch (e) { //alert(e.message); } }}); }); /** * Traverse a tree and return matching nodes. * @param {Object} tree - tree to traverse * @param {String} prop - node property to match * @param {Mixed} val - value that node property must match * @param {Boolean} all - return all results rather than stopping at first matching node (optional) * @param {String} children - search node children in property named by this argument (optional) * @return all matched nodes | first matched node | null */ function vboxTraverse(tree,prop,val,all,children) { var leafs = new Array(); for(var a in tree) { if(tree[a][prop] == val) { if(!all) return tree[a]; leafs[leafs.length] = tree[a]; } if(children && tree[a][children] && tree[a][children].length) { var c = vboxTraverse(tree[a][children],prop,val,all,children); if(!all && c) { return c; } else if(c && c.length) { leafs = leafs.concat(c); } } } return (all ? leafs : null); } /** * Performs AJAX request, alert()'s returned errors * * @param {String} fn - AJAX function to call * @param {Object} params - params to pass to AJAX call * @return {Object} deferred promise */ function vboxAjaxRequest(fn,params,config) { // Promise for data var def = $.Deferred(); // Fatal error previously occurred if($('#vboxPane').data('vboxFatalError')) return def.reject(); var data = { 'fn': fn, 'params': params ? params : null, 'persist': config && config.persist ? config.persist : null }; $.when($.post(vboxEndpointConfig.api, JSON.stringify(data), undefined,"json") // Run on error .fail(function(d,etext,xlr,d2) { // Fatal error previously occurred if($('#vboxPane').data('vboxFatalError')) return null; if(etext != 'error') { // Halt on parse errors if(etext.search(/parse/i) > -1) { $('#vboxPane').data('vboxFatalError',1); } if(window.console && window.console.log) window.console.log(etext + ': '+ d.responseText); vboxAlert({'error':'Ajax error: ' + etext,'details':d.responseText},{'width':'400px'}); } else { // Check for error HTTP status if(d && d.status && (String(d.status).substring(0,1) == '4' || String(d.status).substring(0,1) == '5')) { var err = {error:'
'+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')+'
'+s,{'width':'50%'}); // Ignore connection errors until we have config data unless this was a login attempt } else if(!d.errors[i].connection || fn == 'login') { // If we have config data, and the error is fatal, halt processing if(d.errors[i].fatal && $('#vboxPane').data('vboxConfig')) { $('#vboxPane').data('vboxFatalError',1); $('#vboxPane').css({'display':'none'}); } vboxAlert(d.errors[i],{'width':'400px'}); } } else { // Error from normal request vboxAlert(d.errors[i],{'width':'400px'}); } } // foreach error > } // if errors.length > return (d && d.data ? d.data : null); }) ).done(function(d) { if(d) def.resolve(d); else def.reject(); }).fail(function(){ def.reject(); }); return def.promise(); } /** * Return VRDE host address of VM * @param {Object} vm - virtual machine object * @return {String} VRDE host for VM */ function vboxGetVRDEHost(vm) { var chost = ($('#vboxPane').data('vboxConfig').consoleHost ? $('#vboxPane').data('vboxConfig').consoleHost : (vm && vm.VRDEServer && vm.VRDEServer.netAddress ? vm.VRDEServer.netAddress : null)); if(!chost) { // Set to host chost = $('#vboxPane').data('vboxConfig').host; // Check for localhost / 127.0.0.1 if(!chost || chost == 'localhost' || chost == '127.0.0.1') chost = location.hostname; } return chost; } /** * Return the correct icon string relative to images/vbox/ for the guest OS type * @param {String} osTypeId - guest OS type id * @return {String} icon file name */ function vboxGuestOSTypeIcon(osTypeId) { var strIcon = "os_other.png"; switch (osTypeId) { case "Other": strIcon = "os_other.png"; break; case "DOS": strIcon = "os_dos.png"; break; case "Netware": strIcon = "os_netware.png"; break; case "L4": strIcon = "os_l4.png"; break; case "Windows31": strIcon = "os_win31.png"; break; case "Windows95": strIcon = "os_win95.png"; break; case "Windows98": strIcon = "os_win98.png"; break; case "WindowsMe": strIcon = "os_winme.png"; break; case "WindowsNT4": strIcon = "os_winnt4.png"; break; case "Windows2000": strIcon = "os_win2k.png"; break; case "WindowsXP": strIcon = "os_winxp.png"; break; case "WindowsXP_64": strIcon = "os_winxp_64.png"; break; case "Windows2003": strIcon = "os_win2k3.png"; break; case "Windows2003_64": strIcon = "os_win2k3_64.png"; break; case "WindowsVista": strIcon = "os_winvista.png"; break; case "WindowsVista_64": strIcon = "os_winvista_64.png"; break; case "Windows2008": strIcon = "os_win2k8.png"; break; case "Windows2008_64": strIcon = "os_win2k8_64.png"; break; case "Windows7": strIcon = "os_win7.png"; break; case "Windows7_64": strIcon = "os_win7_64.png"; break; case "Windows8": strIcon = "os_win8.png"; break; case "Windows8_64": strIcon = "os_win8_64.png"; break; case "Windows81": strIcon = "os_win81.png"; break; case "Windows81_64": strIcon = "os_win81_64.png"; break; case "Windows10": strIcon = "os_win10.png"; break case "Windows10_64": strIcon = "os_win10.png"; break case "WindowsNT": strIcon = "os_win_other.png"; break; case "WindowsNT_64": strIcon = "os_win_other_64.png"; break; case "Windows2012_64": strIcon = "os_win2k12_64.png"; break; case "Windows2016_64": strIcon = "os_win2k16_64.png"; break; case "OS2Warp3": strIcon = "os_os2warp3.png"; break; case "OS2Warp4": strIcon = "os_os2warp4.png"; break; case "OS2Warp45": strIcon = "os_os2warp45.png"; break; case "OS2eCS": strIcon = "os_os2ecs.png"; break; case "OS2": strIcon = "os_os2_other.png"; break; case "Linux_64": strIcon = "os_linux_64.png"; break; case "Linux": strIcon = "os_linux.png"; break; case "Linux22": strIcon = "os_linux22.png"; break; case "Linux24": strIcon = "os_linux24.png"; break; case "Linux24_64": strIcon = "os_linux24_64.png"; break; case "Linux26": strIcon = "os_linux26.png"; break; case "Linux26_64": strIcon = "os_linux26_64.png"; break; case "ArchLinux": strIcon = "os_archlinux.png"; break; case "ArchLinux_64": strIcon = "os_archlinux_64.png"; break; case "Debian": strIcon = "os_debian.png"; break; case "Debian_64": strIcon = "os_debian_64.png"; break; case "OpenSUSE": strIcon = "os_opensuse.png"; break; case "OpenSUSE_64": strIcon = "os_opensuse_64.png"; break; case "Fedora": strIcon = "os_fedora.png"; break; case "Fedora_64": strIcon = "os_fedora_64.png"; break; case "Gentoo": strIcon = "os_gentoo.png"; break; case "Gentoo_64": strIcon = "os_gentoo_64.png"; break; case "Mandriva": strIcon = "os_mandriva.png"; break; case "Mandriva_64": strIcon = "os_mandriva_64.png"; break; case "RedHat": strIcon = "os_redhat.png"; break; case "RedHat_64": strIcon = "os_redhat_64.png"; break; case "Turbolinux": strIcon = "os_turbolinux.png"; break; case "Turbolinux_64": strIcon = "os_turbolinux_64.png"; break; case "Ubuntu": strIcon = "os_ubuntu.png"; break; case "Ubuntu_64": strIcon = "os_ubuntu_64.png"; break; case "Xandros": strIcon = "os_xandros.png"; break; case "Xandros_64": strIcon = "os_xandros_64.png"; break; case "FreeBSD": strIcon = "os_freebsd.png"; break; case "FreeBSD_64": strIcon = "os_freebsd_64.png"; break; case "OpenBSD": strIcon = "os_openbsd.png"; break; case "OpenBSD_64": strIcon = "os_openbsd_64.png"; break; case "NetBSD": strIcon = "os_netbsd.png"; break; case "NetBSD_64": strIcon = "os_netbsd_64.png"; break; case "Solaris": strIcon = "os_solaris.png"; break; case "Solaris_64": strIcon = "os_solaris_64.png"; break; case "Solaris11_64": strIcon = "os_oraclesolaris_64.png"; break; case "OpenSolaris": strIcon = "os_oraclesolaris.png"; break; case "OpenSolaris_64": strIcon = "os_oraclesolaris_64.png"; break; case "QNX": strIcon = "os_qnx.png"; break; case "MacOS106": strIcon = "os_macosx.png"; break; case 'MacOS': strIcon = "os_macosx.png"; break; case "MacOS106_64": strIcon = "os_macosx_64.png"; break; case 'MacOS_64': strIcon = "os_macosx_64.png"; break; case 'Oracle': strIcon = "os_oracle.png"; break; case 'Oracle_64': strIcon = "os_oracle_64.png"; break; case 'JRockitVE': strIcon = 'os_jrockitve.png'; break; case "VirtualBox_Host": strIcon = "os_virtualbox.png"; break; default: break; } return strIcon; } /** * Return the correct icon relative to images/vbox/ for the VM state. * @param {String} state - virtual machine state * @return {String} icon file name */ function vboxMachineStateIcon(state) { var strIcon = "state_powered_off_16px.png"; var strNoIcon = "state_running_16px.png"; switch (state) { case "PoweredOff": strIcon = "state_powered_off_16px.png"; break; case "Saved": strIcon = "state_saved_16px.png"; break; case "Saving": strIcon = "state_saving_16px.png"; break; case "Snapshotting": strIcon = "snapshot_offline_16px.png"; break; case "LiveSnapshotting": strIcon = "snapshot_online_16px.png"; break; case "Aborted": strIcon = "state_aborted_16px.png"; break; case "Running": strIcon = "state_running_16px.png"; break; case "Paused": strIcon = "state_paused_16px.png"; break; case "Stuck": strIcon = "state_stuck_16px.png"; break; case "Saving": strIcon = "state_discarding_16px.png"; break; case "Restoring": strIcon = "vm_settings_16px.png"; break; case "RestoringSnapshot": strIcon = "discard_cur_state_16px.png"; break; case "DeletingSnapshot": strIcon = "state_discarding_16px.png"; break; case "Hosting" : strIcon = "vm_settings_16px.png"; break; case "Inaccessible": strIcon = "state_aborted_16px.png"; break; default: strIcon = strNoIcon; } return strIcon; } /** * File or Folder browser dialog * @param {String} root - path to initial folder or file * @param {Function} fn - callback function to run when OK is clicked on dialog * @param {Boolean} foldersonly - only display / allow selection of folders (optional) * @param {String} title - title of dialog (optional) * @param {String} icon - URL to icon (optional) * @param {Boolean} strictFiles - only allow the OK button to be clicked when a file is selected (optional) */ function vboxFileBrowser(root,fn,foldersonly,title,icon,strictFiles) { var buttons = { }; buttons[trans('OK','QIMessageBox')] = function(f) { if(strictFiles && $('#vboxBrowseFolderList').find('.vboxListItemSelected').first().parent().hasClass('directory')) { $('#vboxBrowseFolderList').find('.vboxListItemSelected').first().trigger('dblclick'); return; } if(typeof f != 'string') { f = $('#vboxBrowseFolderList').find('.vboxListItemSelected').first().attr('name'); } $('#vboxBrowseFolder').trigger('close').empty().remove(); fn(f); }; buttons[trans('Cancel','QIMessageBox')] = function() { fn(null); $('#vboxBrowseFolder').trigger('close').empty().remove(); }; var d1 = $('').attr({'id':'vboxBrowseFolder','class':'vboxDialogContent','style':'display:none'}); $('').attr({'id':'vboxBrowseFolderList'}).fileTree({ 'root': (root ? root : '/'),'dirsOnly':foldersonly,'loadMessage':trans('Loading ...','UIVMDesktop'),'scrollTo':'#vboxBrowseFolder'},function(f){ buttons[trans('OK','QIMessageBox')](f); }).appendTo(d1); $(d1).dialog({'closeOnEscape':true,'width':400,'height':600,'buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent','title':' ' + (title ? title : trans((foldersonly ? 'Select Folder' : 'Select File')))}).on("dialogbeforeclose",function(){ $(this).parent().find('span:contains("'+trans('Cancel','QIMessageBox')+'")').trigger('click'); }); } /** * Convert megabytes to human readable string * @param {Integer} mb - megabytes * @return {String} human readable size representation (e.g. 2 GB, 500 MB, etc..) */ function vboxMbytesConvert(mb) {return vboxBytesConvert(parseFloat(mb) * 1024 * 1024);} /** * Convert bytes to human readable string * @param {Integer} bytes - bytes * @return {String} human readable size representation (e.g. 2 GB, 500 MB, etc..) */ function vboxBytesConvert(bytes) { var ext = new Array('B','KB','MB','GB','TB'); var unitCount; for(unitCount=0; bytes >= 1024 && unitCount < ext.length; unitCount++) bytes = parseFloat(parseFloat(bytes)/1024); return Math.round(parseFloat(bytes)*Math.pow(10,2))/Math.pow(10,2) + " " + trans(ext[unitCount], 'VBoxGlobal'); } /** * Parse str param into megabytes * @param {String} str - size string (2 TB, 500 MB, etc..) to parse * @return {Integer} megabytes */ function vboxConvertMbytes(str) { str = str.replace(' ',' '); str = str.split(' ',2); if(!str[1]) str[1] = trans('MB','VBoxGlobal'); var ext = new Array(trans('B','VBoxGlobal'),trans('KB','VBoxGlobal'),trans('MB','VBoxGlobal'),trans('GB','VBoxGlobal'),trans('TB','VBoxGlobal')); var index = jQuery.inArray(str[1],ext); if(index == -1) index = 2; switch(index) { case 0: return ((str[0] / 1024) / 1024); break; case 1: return (str[0] / 1024); break; case 3: return (str[0] * 1024); break; case 4: return (str[0] * 1024 * 1024); break; default: return (str[0]); } } /** * Display alert Dialog * @param {String|Object} e - message to display or object containing error message and details * @param {Object} xtraOpts - extra options to apply to alert jquery dialog (optional) * @see jQuery.dialog() */ function vboxAlert(e,xtraOpts) { var acknowledged = $.Deferred(); var msg = ''; if(typeof e == 'object') msg = e.error; else msg = e; // Convert toif(msg[0] != '<') msg = '
'+msg+'
'; var div = $('').attr({'class':'vboxDialogContent vboxAlert'}).html(''+msg); if(typeof e == 'object' && e.details) { // Details can contain HTML entities e.details = $('').html(e.details).text(); var p = $('').attr({'style':'text-align: center'}); $('').attr({'href':'#'}).html(trans('Details','QIMessageBox')).click(function(){ $(this).parent().parent().dialog('option',{'height':400,'position':'center'}); $(this).parent().siblings(".vboxAlert").css({"display":""}); $(this).parent().css({'padding':'0px','margin':'0px'}); $(this).parent().siblings(".vboxAlert").siblings().empty().remove(); return false; }).appendTo(p); $(div).append(p); var ddet = $('').attr({'style':'display: none; height: 100%; width: auto;','class':'vboxAlert'}); $('').attr({'spellcheck':'false','wrap':'off','readonly':'true'}).val(e.details).appendTo($('').appendTo(ddet)); $(div).append(ddet); } var buttons = { }; buttons[trans('OK','QIMessageBox')] = function(f) { $(this).trigger('close').empty().remove(); acknowledged.resolve(); }; var dialogOpts = {'closeOnEscape':false,'width':600,'height':'auto','buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent','title':' phpVirtualBox'}; if(typeof xtraOpts == "object") { for(var i in xtraOpts) { dialogOpts[i] = xtraOpts[i]; } } $(div).dialog(dialogOpts); return acknowledged; } /** * Confirmation dialog * @param {String} q - question to ask * @param {Object} buttons -buttons to display on confirmation dialog * @param {String} cancelText - string displayed on Cancel button. Defaults to 'Cancel' * @param {Function} onCancel - function to run onCancel * @return {HTMLNode} * @see jQuery.dialog() */ function vboxConfirm(q,buttons,cancelText,onCancel) { var div = $('').attr({'class':'vboxDialogContent','style':'display: none; width: 500px;'}).html(''+q); if(!cancelText) cancelText = trans('Cancel','QIMessageBox'); buttons[cancelText] = function() { $(this).remove(); if(onCancel) { onCancel(); }}; $(div).dialog({'closeOnEscape':false,'width':500,'height':'auto','buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent','title':' phpVirtualBox'}); return $(div); } /** * Initialize common UI items * @param {String|HTMLNode} root - root HTML Node or node ID to initialize * @param {String} context - language context to use for translations * @see trans() */ function vboxInitDisplay(root,context) { if(typeof root == 'string') root = $('#'+root); /* * Sliders */ $(root).find('div.slider').each(function(){ if($(this).hasClass('translateglob')) { $(this).closest('table').find(".translate").html(function(i,h){return trans($('').html(h).text(),'VBoxGlobal');}).removeClass('translate'); } var frm = $(this).data('form'); if($(this).data('display')) { var fn = $(this).data('display'); $(this).slider('option','slide',function(event,ui){ document.forms[frm].elements[event.target.id + 'Value'].value = fn(ui.value); }).slider('option','change',function(event,ui){ document.forms[frm].elements[event.target.id + 'Value'].value = fn(ui.value); }); } else { $(this).slider('option','slide',function(event,ui){ document.forms[frm].elements[event.target.id + 'Value'].value = ui.value; }).slider('option','change',function(event,ui){ document.forms[frm].elements[event.target.id + 'Value'].value = ui.value; }); } // Slider scale (ticks) $(this).children("div.sliderScale").each(function(){ var min = $(this).parent().slider('option','min'); var max = $(this).parent().slider('option','max'); var diff = Math.min((max - min),50); var tdw = Math.round(100 / diff); var tr = $('The list of virtual machines will not begin auto-refreshing again until this page is reloaded.
','phpVirtualBox')+(msg ? msg : '')); } else { 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.The list of virtual machines will not begin auto-refreshing again until this page is reloaded.
','phpVirtualBox') + msg.error; vboxAlert(msg); } } /** * Set a cookie and update $('#vboxPane').data('vboxCookies') * @param {String} k - cookie key * @param {any} v - cookie value * @param {Date} expire - when cookie should expire */ function vboxSetCookie(k,v,expire) { var exp = (v ? (expire ? expire : new Date(2020,12,24)) : new Date().setDate(new Date().getDate() - 1)); document.cookie = k+"="+v+"; expires="+exp.toGMTString()+"; path=/"; $('#vboxPane').data('vboxCookies')[k] = v; } /** * Set a local data item using the local storage mechanism * and upate $('#vboxPane').data('vboxCookies'); * @param {String} k - data item key * @param {any} v - data item value * @param {Boolean} nocookies - do not fall back to cookies */ function vboxSetLocalDataItem(k,v,nocookies) { // fall back to normal cookie if(typeof(Storage)==="undefined") { if(!nocookies) vboxSetCookie(k,v); return; } // Remove item? if(v) { localStorage.setItem(k,v.toString()); } else { localStorage.removeItem(k); } } /** * Get a local data item using the local storage mechanism * @param {String} k - data item key * @return {mixed} data item value */ function vboxGetLocalDataItem(k) { // fall back to normal cookie if(typeof(Storage)==="undefined") { return $('#vboxPane').data('vboxCookies')[k]; } return localStorage.getItem(k); } /** * Strip file name from path * @param {String} p - path * @return {String} path minus file name */ function vboxDirname(p) { var pos = p.lastIndexOf($('#vboxPane').data('vboxConfig').DSEP); if(pos > -1) { return p.substring(0,pos); } return p; } /** * Strip dir name from path * @param {String} p - path * @return {String} file name portion of path */ function vboxBasename(p) { var pos = p.lastIndexOf($('#vboxPane').data('vboxConfig').DSEP); if(pos > -1) { return p.substring((pos+1)); } return p; } /** * Return a time or date+time string depending on * how much time has elapsed * @param {Integer} t - seconds since 1/1/1970 0:0:0 * @param {String} replaceTime - optional string to return replacing time * @param {String} replaceDateTime - optional string to return replace date_time * @return {String} time or date+time string */ function vboxDateTimeString(t, replaceTime, replaceDateTime) { var sdate = new Date(t*1000); if((new Date().getTime() - sdate.getTime())/1000 > 86400 || new Date().getDate() != sdate.getDate()) { return (replaceDateTime ? replaceDateTime.replace('%1',sdate.toLocaleString()) : sdate.toLocaleString()); } return (replaceTime ? replaceTime.replace('%1',sdate.toLocaleTimeString()) : sdate.toLocaleTimeString()); } /** * Calculate scrollbar width * @return {Integer} width of scrollbar * * http://www.alexandre-gomes.com/?p=115 * */ var getScrollbarWidth = function() { var inner = document.createElement('p'); inner.style.width = "100%"; inner.style.height = "200px"; var outer = document.createElement('div'); outer.style.position = "absolute"; outer.style.top = "0px"; outer.style.left = "0px"; outer.style.visibility = "hidden"; outer.style.width = "200px"; outer.style.height = "150px"; outer.style.overflow = "hidden"; outer.appendChild (inner); document.body.appendChild (outer); var w1 = inner.offsetWidth; outer.style.overflow = 'scroll'; var w2 = inner.offsetWidth; if (w1 == w2) w2 = outer.clientWidth; document.body.removeChild (outer); return (w1 - w2); }; /** * Returns the result of case-insensitive string comparison using 'natural' algorithm comparing str1 to str2 * @param {String} str1 - 1st string * @param {String} str2 - 2nd string * @return {Integer} integer for use in list sorting comparison */ function strnatcasecmp(str1, str2) { // Returns the result of case-insensitive string comparison using 'natural' algorithm // // version: 1004.2314 // discuss at: http://phpjs.org/functions/strnatcasecmp // + original by: Martin Pool // + reimplemented by: Pierre-Luc Paour // + reimplemented by: Kristof Coomans (SCK-CEN (Belgian Nucleair Research Centre)) // + reimplemented by: Brett Zamir (http://brett-zamir.me) // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // * example 1: strnatcasecmp(10, 1); // * returns 1: 1 // * example 1: strnatcasecmp('1', '10'); // * returns 1: -1 var a = (str1+'').toLowerCase(); var b = (str2+'').toLowerCase(); var isWhitespaceChar = function (a) { return a.charCodeAt(0) <= 32; }; var isDigitChar = function (a) { var charCode = a.charCodeAt(0); return ( charCode >= 48 && charCode <= 57 ); }; var compareRight = function (a,b) { var bias = 0; var ia = 0; var ib = 0; var ca; var cb; // The longest run of digits wins. That aside, the greatest // value wins, but we can't know that it will until we've scanned // both numbers to know that they have the same magnitude, so we // remember it in BIAS. for (;; ia++, ib++) { ca = a.charAt(ia); cb = b.charAt(ib); if (!isDigitChar(ca) && !isDigitChar(cb)) { return bias; } else if (!isDigitChar(ca)) { return -1; } else if (!isDigitChar(cb)) { return +1; } else if (ca < cb) { if (bias == 0) { bias = -1; } } else if (ca > cb) { if (bias == 0) { bias = +1; } } else if (ca == 0 && cb == 0) { return bias; } } }; var ia = 0, ib = 0; var nza = 0, nzb = 0; var ca, cb; var result; while (true) { // only count the number of zeroes leading the last number compared nza = nzb = 0; ca = a.charAt(ia); cb = b.charAt(ib); // skip over leading spaces or zeros while (isWhitespaceChar( ca ) || ca =='0') { if (ca == '0') { nza++; } else { // only count consecutive zeroes nza = 0; } ca = a.charAt(++ia); } while (isWhitespaceChar( cb ) || cb == '0') { if (cb == '0') { nzb++; } else { // only count consecutive zeroes nzb = 0; } cb = b.charAt(++ib); } // process run of digits if (isDigitChar(ca) && isDigitChar(cb)) { if ((result = compareRight(a.substring(ia), b.substring(ib))) != 0) { return result; } } if (ca == 0 && cb == 0) { // The strings compare the same. Perhaps the caller // will want to call strcmp to break the tie. return nza - nzb; } if (ca < cb) { return -1; } else if (ca > cb) { return +1; } ++ia; ++ib; } } /** Filter prototype for older browsers * https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter */ if (!Array.prototype.filter) { Array.prototype.filter = function(fun /*, thisp */) { "use strict"; if (this == null) throw new TypeError(); var t = Object(this); var len = t.length >>> 0; if (typeof fun != "function") throw new TypeError(); var res = []; var thisp = arguments[1]; for (var i = 0; i < len; i++) { if (i in t) { var val = t[i]; // in case fun mutates this if (fun.call(thisp, val, i, t)) res.push(val); } } return res; }; } $(document).ready(function() { // Don't unload while progress operation is .. in progress window.onbeforeunload = vboxOpInProgressCheck; });