diff options
author | Tudor Holton <tudor@tudorholton.com> | 2018-07-13 17:36:48 +1000 |
---|---|---|
committer | Tudor Holton <tudor@tudorholton.com> | 2018-07-13 17:36:48 +1000 |
commit | 3c61d5eae51e27082945f629d0076edf0c309f96 (patch) | |
tree | 01146078eecd1a0fc610ae225192debed7925999 /js | |
parent | 013a2e172f2a3bf5cb739dda2c120306918a35d7 (diff) | |
download | phpvirtualbox-3c61d5eae51e27082945f629d0076edf0c309f96.zip phpvirtualbox-3c61d5eae51e27082945f629d0076edf0c309f96.tar.gz phpvirtualbox-3c61d5eae51e27082945f629d0076edf0c309f96.tar.bz2 |
Convert from DOS to UNIX format. Update changelog for release.
Diffstat (limited to 'js')
-rw-r--r-- | js/canvasimages.js | 1296 | ||||
-rw-r--r-- | js/chooser.js | 4900 | ||||
-rw-r--r-- | js/datamediator.js | 928 | ||||
-rw-r--r-- | js/dialogs.js | 3450 | ||||
-rw-r--r-- | js/eventlistener.js | 500 | ||||
-rw-r--r-- | js/jquery.jec-1.3.1.js | 1720 | ||||
-rw-r--r-- | js/jquery.scrollTo-min.js | 20 | ||||
-rw-r--r-- | js/utils.js | 2752 |
8 files changed, 7783 insertions, 7783 deletions
diff --git a/js/canvasimages.js b/js/canvasimages.js index eba4279..5d80b6c 100644 --- a/js/canvasimages.js +++ b/js/canvasimages.js @@ -1,648 +1,648 @@ -/**
- * @fileOverview HTML5 canvas image functions
- * @author Ian Moore (imoore76 at yahoo dot com)
- * @version $Id: canvasimages.js 591 2015-04-11 22:40:47Z imoore76 $
- * @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com)
- */
-
-/**
- * Returns true if broswer supports canvas
- * @return {Boolean} true if broswer supports canvas
- */
-var __vboxIsCanvasSupported = null; // cached
-var isCanvasSupported = function(){
- if(__vboxIsCanvasSupported === null) {
- try {
- var elem = document.createElement('canvas');
- __vboxIsCanvasSupported = !!(elem && elem.getContext && elem.getContext('2d'));
- } catch (err) {
- __vboxIsCanvasSupported = false;
- }
- }
- return __vboxIsCanvasSupported;
-};
-
-var __vboxPreviewCanvasCache = [];
-function vboxDrawPreviewCanvas(can, imageObj, text, width, height) {
-
- var screenMargin = 7;
- var margin = 10;
-
- var resizeToImage = (imageObj && (imageObj.width == width));
-
- // Height / width comes from direct image values
- if(imageObj && resizeToImage) {
-
- height = imageObj.height;
- width = imageObj.width;
-
- // Set height while maintaining aspect ratio
- } else if (imageObj) {
-
- height = imageObj.height * (width/imageObj.width);
- }
-
- // Margins are added to width
- height += ((margin+screenMargin)*2);
- width += ((margin+screenMargin)*2);
-
- // Does canvas still exist?
- // VM selection can change while this function is running
- // in which case the canvas goes away
- if(!can) return;
-
- // Set canvas values
- can.height = height;
- can.width = width;
-
- var ctx = can.getContext('2d');
-
-
- // Clear the canvas
- ctx.clearRect(0,0,width,height);
-
- ctx.save();
-
- // Draw and cache monitor image if it is not present
- if(!__vboxPreviewCanvasCache[width+'x'+height]) {
-
- var cachedCanvas = document.createElement('canvas');
-
- cachedCanvas.width = width;
- cachedCanvas.height = height;
-
- var cachedCtx = cachedCanvas.getContext('2d');
-
- cachedCtx.beginPath();
- cachedCtx.strokeStyle = "#000000";
- cachedCtx.lineWidth = 0.3;
- cachedCtx.lineCap = 'butt';
-
- cachedCtx.moveTo(margin*2,margin);
-
- // top and top right
- cachedCtx.lineTo(width-(margin*2), margin);
- cachedCtx.arcTo(width-margin, margin, width-margin,margin*2, margin);
-
- // Side and bottom right
- cachedCtx.lineTo(width-margin, height-(margin*2));
- cachedCtx.arcTo(width-margin, height-margin, width-(margin*2), height-margin, margin);
-
- // bottom and bottom left
- cachedCtx.lineTo(margin*2, height-margin);
- cachedCtx.arcTo(margin, height-margin, margin, height-(margin*2), margin);
-
- // Left line and top left
- cachedCtx.lineTo(margin, margin*2);
- cachedCtx.arcTo(margin, margin, margin * 2, margin, margin);
-
- cachedCtx.closePath();
- cachedCtx.save();
- cachedCtx.shadowOffsetX = 5;
- cachedCtx.shadowOffsetY = 5;
- cachedCtx.shadowBlur = 4;
- cachedCtx.shadowColor = "rgba(30, 30, 30, 0.2)";
-
-
- var grad = cachedCtx.createLinearGradient(0, margin, 0, height);
- grad.addColorStop(0, "rgb(200,200,200)");
- grad.addColorStop(0.4, "rgb(100,100,100)");
- grad.addColorStop(0.5, "rgb(66,66,66)");
- grad.addColorStop(0.7, "rgb(100,100,100)");
- grad.addColorStop(1, "rgb(200,200,200)");
-
- cachedCtx.fillStyle = grad;
-
- cachedCtx.fill();
-
- // Redraw so that shadow is seen on all sides
- cachedCtx.shadowOffsetX = -5;
- cachedCtx.shadowOffsetY = -5;
- cachedCtx.fill();
- cachedCtx.restore();
- cachedCtx.fillRect(margin+screenMargin,margin+screenMargin,width-(margin*2)-(screenMargin*2),height-(margin*2)-(screenMargin*2));
- cachedCtx.stroke();
- cachedCtx.restore();
-
- var cvs = document.createElement('canvas');
-
- /* Gloss */
- var rectX = 0;
- var rectY = 0;
- var rectWidth = width-(margin+screenMargin)*2;
- var rectHeight = height-(margin+screenMargin)*2;
-
- cvs.width = rectWidth;
- cvs.height = rectHeight;
-
- var ctxBlur = cvs.getContext('2d');
- ctxBlur.beginPath();
- ctxBlur.lineWidth = 1;
- ctxBlur.strokeStyle = "#000000";
- ctxBlur.moveTo(rectX,rectY);
- ctxBlur.lineTo(rectWidth, rectY);
- ctxBlur.lineTo(rectWidth,rectHeight*1.0/3.0);
- ctxBlur.bezierCurveTo(rectX+rectWidth / 2.0, rectY + rectHeight * 1.0/3.0,
- rectX+rectWidth / 2.0, rectY + rectHeight * 2.0/3.0,
- rectX, rectY + rectHeight * 2.0/3.0);
- ctxBlur.closePath();
- ctxBlur.fillStyle="rgba(255,255,255,0.3)";
- ctxBlur.fill();
-
- stackBlurCanvasRGBA( cvs, 0, 0, rectWidth, rectHeight, 17 );
-
- ctx.drawImage(cvs, margin+screenMargin, margin+screenMargin, rectWidth, rectHeight);
-
- __vboxPreviewCanvasCache[width+'x'+height] = {
- 'monitor' : cachedCanvas,
- 'gloss' : cvs
- };
-
- }
-
- // Draw cached monitor canvas
- ctx.drawImage(__vboxPreviewCanvasCache[width+'x'+height]['monitor'], 0, 0, width, height);
-
- /* Screenshot */
- if(imageObj) {
-
- ctx.drawImage(imageObj, 0, 0, imageObj.width, imageObj.height, (margin+screenMargin), (margin+screenMargin), width-(margin*2)-(screenMargin*2),height-(margin*2)-(screenMargin*2));
- }
-
- // Draw cached gloss canvas
- ctx.drawImage(__vboxPreviewCanvasCache[width+'x'+height]['gloss'], 0, 0, width, height);
-
- /* Text */
- if(!imageObj) {
-
- txtCan = document.createElement('canvas');
- txtCan.width = width-(margin+screenMargin)*2;
- txtCan.height = height-(margin+screenMargin)*2;
-
- fitTextToCanvas(txtCan, text, 18);
-
- ctx.drawImage(txtCan, (margin+screenMargin), (margin+screenMargin));
-
- }
-
-
- return;
-}
-
-
-/* HELPERS */
-
-/**
- * Fit text to a canvas element
- *
- * @author Ian Moore
- */
-var fitTextToCanvas = function(can, text, fontSize) {
-
- var lineHeightOffset = 1.4;
- var lineHeight = fontSize * lineHeightOffset;
- var minFontSize = 10;
- var padding = 2;
-
- var words = text.split(" ");
-
- var maxWidth = can.width;
- var maxHeight = can.height;
- var context = can.getContext('2d');
- context.moveTo(0,0);
-
-
- var wrapTextLines = function() {
-
- context.font = "bold " + fontSize + "pt Arial";
-
- var line = '';
- var lines = [];
-
- for (var n = 0; n < words.length; n++) {
-
- var testLine = line + (line.length ? ' ' : '') + words[n];
-
- if((context.measureText(testLine).width + padding) > maxWidth) {
-
- // Only one word is too big
- if(testLine.indexOf(' ') == -1) {
-
- if(fontSize > minFontSize) {
- fontSize *= 0.9;
- return wrapTextLines();
- }
- line = testLine;
-
- } else {
- lines[lines.length] = line;
- line = words[n];
- }
-
- } else {
-
- line = testLine;
-
- }
-
- }
- if(line.length) {
- if((context.measureText(line).width + padding) > maxWidth && fontSize > minFontSize) {
- fontSize *= 0.9;
- return wrapTextLines();
- }
- lines[lines.length] = line;
- }
- return lines;
-
- };
-
- // Initial wrap
- lines = wrapTextLines();
-
- // Since text will be aligned to the bottom, we subtract
- // one lineheight addition because it will be off the
- // visible canvas and should not be included in calculations
- while(((lines.length * lineHeight)-(lineHeight-fontSize) > maxHeight) && fontSize > minFontSize) {
- fontSize *= 0.9;
- lines = wrapTextLines();
- lineHeight = fontSize * lineHeightOffset;
- }
-
- context.fillStyle = "#ffffff";
- context.textAlign = "center";
- context.textBaseline = 'alphabetic';
- context.strokeStyle = "#ff0000";
-
- var totalHeight = Math.round((lines.length * lineHeight)-(lineHeight-fontSize));
-
- for(var i = 0; i < lines.length; i++) {
- /*
- Uncomment to debug line heights
-
- context.moveTo(0, (maxHeight/2)-(totalHeight/2)+(lineHeight*i));
- context.lineTo(maxWidth, (maxHeight/2)-(totalHeight/2)+(lineHeight*i));
- context.stroke();
- context.moveTo(0, (maxHeight/2)-(totalHeight/2)+(lineHeight*i)+lineHeight);
- context.lineTo(maxWidth, (maxHeight/2)-(totalHeight/2)+(lineHeight*i)+lineHeight);
- context.stroke();
- */
- context.fillText(lines[i],(maxWidth/2),(maxHeight/2)-(totalHeight/2)+(lineHeight*i)+lineHeight-(lineHeight-fontSize));
- }
- return can;
-
-};
-
-/*
-
-StackBlur - a fast almost Gaussian Blur For Canvas
-
-Version: 0.5
-Author: Mario Klingemann
-Contact: mario@quasimondo.com
-Website: http://www.quasimondo.com/StackBlurForCanvas
-Twitter: @quasimondo
-
-In case you find this class useful - especially in commercial projects -
-I am not totally unhappy for a small donation to my PayPal account
-mario@quasimondo.de
-
-Or support me on flattr:
-https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript
-
-Copyright (c) 2010 Mario Klingemann
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without
-restriction, including without limitation the rights to use,
-copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-var mul_table = [
- 512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,
- 454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,
- 482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,
- 437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,
- 497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,
- 320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,
- 446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,
- 329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,
- 505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,
- 399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,
- 324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,
- 268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,
- 451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,
- 385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,
- 332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,
- 289,287,285,282,280,278,275,273,271,269,267,265,263,261,259];
-
-
-var shg_table = [
- 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
- 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
- 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
- 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
- 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
- 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
- 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
- 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
- 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
- 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
- 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
- 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 ];
-
-var stackBlurCanvasRGBA = function( canvas, top_x, top_y, width, height, radius )
-{
- if ( isNaN(radius) || radius < 1 ) return;
- radius |= 0;
-
- //var canvas = document.getElementById( id );
- var context = canvas.getContext("2d");
- var imageData;
-
- try {
- try {
- imageData = context.getImageData( top_x, top_y, width, height );
- } catch(e) {
-
- // NOTE: this part is supposedly only needed if you want to work with local files
- // so it might be okay to remove the whole try/catch block and just use
- // imageData = context.getImageData( top_x, top_y, width, height );
- try {
- netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
- imageData = context.getImageData( top_x, top_y, width, height );
- } catch(e) {
- alert("Cannot access local image");
- throw new Error("unable to access local image data: " + e);
- return;
- }
- }
- } catch(e) {
- alert("Cannot access image");
- throw new Error("unable to access image data: " + e);
- }
-
- var pixels = imageData.data;
-
- var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum,
- r_out_sum, g_out_sum, b_out_sum, a_out_sum,
- r_in_sum, g_in_sum, b_in_sum, a_in_sum,
- pr, pg, pb, pa, rbs;
-
- var div = radius + radius + 1;
- var widthMinus1 = width - 1;
- var heightMinus1 = height - 1;
- var radiusPlus1 = radius + 1;
- var sumFactor = radiusPlus1 * ( radiusPlus1 + 1 ) / 2;
-
- var stackStart = new BlurStack();
- var stack = stackStart;
- for ( i = 1; i < div; i++ )
- {
- stack = stack.next = new BlurStack();
- if ( i == radiusPlus1 ) var stackEnd = stack;
- }
- stack.next = stackStart;
- var stackIn = null;
- var stackOut = null;
-
- yw = yi = 0;
-
- var mul_sum = mul_table[radius];
- var shg_sum = shg_table[radius];
-
- for ( y = 0; y < height; y++ )
- {
- r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0;
-
- r_out_sum = radiusPlus1 * ( pr = pixels[yi] );
- g_out_sum = radiusPlus1 * ( pg = pixels[yi+1] );
- b_out_sum = radiusPlus1 * ( pb = pixels[yi+2] );
- a_out_sum = radiusPlus1 * ( pa = pixels[yi+3] );
-
- r_sum += sumFactor * pr;
- g_sum += sumFactor * pg;
- b_sum += sumFactor * pb;
- a_sum += sumFactor * pa;
-
- stack = stackStart;
-
- for( i = 0; i < radiusPlus1; i++ )
- {
- stack.r = pr;
- stack.g = pg;
- stack.b = pb;
- stack.a = pa;
- stack = stack.next;
- }
-
- for( i = 1; i < radiusPlus1; i++ )
- {
- p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 );
- r_sum += ( stack.r = ( pr = pixels[p])) * ( rbs = radiusPlus1 - i );
- g_sum += ( stack.g = ( pg = pixels[p+1])) * rbs;
- b_sum += ( stack.b = ( pb = pixels[p+2])) * rbs;
- a_sum += ( stack.a = ( pa = pixels[p+3])) * rbs;
-
- r_in_sum += pr;
- g_in_sum += pg;
- b_in_sum += pb;
- a_in_sum += pa;
-
- stack = stack.next;
- }
-
-
- stackIn = stackStart;
- stackOut = stackEnd;
- for ( x = 0; x < width; x++ )
- {
- pixels[yi+3] = pa = (a_sum * mul_sum) >> shg_sum;
- if ( pa != 0 )
- {
- pa = 255 / pa;
- pixels[yi] = ((r_sum * mul_sum) >> shg_sum) * pa;
- pixels[yi+1] = ((g_sum * mul_sum) >> shg_sum) * pa;
- pixels[yi+2] = ((b_sum * mul_sum) >> shg_sum) * pa;
- } else {
- pixels[yi] = pixels[yi+1] = pixels[yi+2] = 0;
- }
-
- r_sum -= r_out_sum;
- g_sum -= g_out_sum;
- b_sum -= b_out_sum;
- a_sum -= a_out_sum;
-
- r_out_sum -= stackIn.r;
- g_out_sum -= stackIn.g;
- b_out_sum -= stackIn.b;
- a_out_sum -= stackIn.a;
-
- p = ( yw + ( ( p = x + radius + 1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2;
-
- r_in_sum += ( stackIn.r = pixels[p]);
- g_in_sum += ( stackIn.g = pixels[p+1]);
- b_in_sum += ( stackIn.b = pixels[p+2]);
- a_in_sum += ( stackIn.a = pixels[p+3]);
-
- r_sum += r_in_sum;
- g_sum += g_in_sum;
- b_sum += b_in_sum;
- a_sum += a_in_sum;
-
- stackIn = stackIn.next;
-
- r_out_sum += ( pr = stackOut.r );
- g_out_sum += ( pg = stackOut.g );
- b_out_sum += ( pb = stackOut.b );
- a_out_sum += ( pa = stackOut.a );
-
- r_in_sum -= pr;
- g_in_sum -= pg;
- b_in_sum -= pb;
- a_in_sum -= pa;
-
- stackOut = stackOut.next;
-
- yi += 4;
- }
- yw += width;
- }
-
-
- for ( x = 0; x < width; x++ )
- {
- g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0;
-
- yi = x << 2;
- r_out_sum = radiusPlus1 * ( pr = pixels[yi]);
- g_out_sum = radiusPlus1 * ( pg = pixels[yi+1]);
- b_out_sum = radiusPlus1 * ( pb = pixels[yi+2]);
- a_out_sum = radiusPlus1 * ( pa = pixels[yi+3]);
-
- r_sum += sumFactor * pr;
- g_sum += sumFactor * pg;
- b_sum += sumFactor * pb;
- a_sum += sumFactor * pa;
-
- stack = stackStart;
-
- for( i = 0; i < radiusPlus1; i++ )
- {
- stack.r = pr;
- stack.g = pg;
- stack.b = pb;
- stack.a = pa;
- stack = stack.next;
- }
-
- yp = width;
-
- for( i = 1; i <= radius; i++ )
- {
- yi = ( yp + x ) << 2;
-
- r_sum += ( stack.r = ( pr = pixels[yi])) * ( rbs = radiusPlus1 - i );
- g_sum += ( stack.g = ( pg = pixels[yi+1])) * rbs;
- b_sum += ( stack.b = ( pb = pixels[yi+2])) * rbs;
- a_sum += ( stack.a = ( pa = pixels[yi+3])) * rbs;
-
- r_in_sum += pr;
- g_in_sum += pg;
- b_in_sum += pb;
- a_in_sum += pa;
-
- stack = stack.next;
-
- if( i < heightMinus1 )
- {
- yp += width;
- }
- }
-
- yi = x;
- stackIn = stackStart;
- stackOut = stackEnd;
- for ( y = 0; y < height; y++ )
- {
- p = yi << 2;
- pixels[p+3] = pa = (a_sum * mul_sum) >> shg_sum;
- if ( pa > 0 )
- {
- pa = 255 / pa;
- pixels[p] = ((r_sum * mul_sum) >> shg_sum ) * pa;
- pixels[p+1] = ((g_sum * mul_sum) >> shg_sum ) * pa;
- pixels[p+2] = ((b_sum * mul_sum) >> shg_sum ) * pa;
- } else {
- pixels[p] = pixels[p+1] = pixels[p+2] = 0;
- }
-
- r_sum -= r_out_sum;
- g_sum -= g_out_sum;
- b_sum -= b_out_sum;
- a_sum -= a_out_sum;
-
- r_out_sum -= stackIn.r;
- g_out_sum -= stackIn.g;
- b_out_sum -= stackIn.b;
- a_out_sum -= stackIn.a;
-
- p = ( x + (( ( p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2;
-
- r_sum += ( r_in_sum += ( stackIn.r = pixels[p]));
- g_sum += ( g_in_sum += ( stackIn.g = pixels[p+1]));
- b_sum += ( b_in_sum += ( stackIn.b = pixels[p+2]));
- a_sum += ( a_in_sum += ( stackIn.a = pixels[p+3]));
-
- stackIn = stackIn.next;
-
- r_out_sum += ( pr = stackOut.r );
- g_out_sum += ( pg = stackOut.g );
- b_out_sum += ( pb = stackOut.b );
- a_out_sum += ( pa = stackOut.a );
-
- r_in_sum -= pr;
- g_in_sum -= pg;
- b_in_sum -= pb;
- a_in_sum -= pa;
-
- stackOut = stackOut.next;
-
- yi += width;
- }
- }
-
- context.putImageData( imageData, top_x, top_y );
-
-};
-
-
-
-var BlurStack = function()
-{
- this.r = 0;
- this.g = 0;
- this.b = 0;
- this.a = 0;
- this.next = null;
-};
+/** + * @fileOverview HTML5 canvas image functions + * @author Ian Moore (imoore76 at yahoo dot com) + * @version $Id: canvasimages.js 591 2015-04-11 22:40:47Z imoore76 $ + * @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com) + */ + +/** + * Returns true if broswer supports canvas + * @return {Boolean} true if broswer supports canvas + */ +var __vboxIsCanvasSupported = null; // cached +var isCanvasSupported = function(){ + if(__vboxIsCanvasSupported === null) { + try { + var elem = document.createElement('canvas'); + __vboxIsCanvasSupported = !!(elem && elem.getContext && elem.getContext('2d')); + } catch (err) { + __vboxIsCanvasSupported = false; + } + } + return __vboxIsCanvasSupported; +}; + +var __vboxPreviewCanvasCache = []; +function vboxDrawPreviewCanvas(can, imageObj, text, width, height) { + + var screenMargin = 7; + var margin = 10; + + var resizeToImage = (imageObj && (imageObj.width == width)); + + // Height / width comes from direct image values + if(imageObj && resizeToImage) { + + height = imageObj.height; + width = imageObj.width; + + // Set height while maintaining aspect ratio + } else if (imageObj) { + + height = imageObj.height * (width/imageObj.width); + } + + // Margins are added to width + height += ((margin+screenMargin)*2); + width += ((margin+screenMargin)*2); + + // Does canvas still exist? + // VM selection can change while this function is running + // in which case the canvas goes away + if(!can) return; + + // Set canvas values + can.height = height; + can.width = width; + + var ctx = can.getContext('2d'); + + + // Clear the canvas + ctx.clearRect(0,0,width,height); + + ctx.save(); + + // Draw and cache monitor image if it is not present + if(!__vboxPreviewCanvasCache[width+'x'+height]) { + + var cachedCanvas = document.createElement('canvas'); + + cachedCanvas.width = width; + cachedCanvas.height = height; + + var cachedCtx = cachedCanvas.getContext('2d'); + + cachedCtx.beginPath(); + cachedCtx.strokeStyle = "#000000"; + cachedCtx.lineWidth = 0.3; + cachedCtx.lineCap = 'butt'; + + cachedCtx.moveTo(margin*2,margin); + + // top and top right + cachedCtx.lineTo(width-(margin*2), margin); + cachedCtx.arcTo(width-margin, margin, width-margin,margin*2, margin); + + // Side and bottom right + cachedCtx.lineTo(width-margin, height-(margin*2)); + cachedCtx.arcTo(width-margin, height-margin, width-(margin*2), height-margin, margin); + + // bottom and bottom left + cachedCtx.lineTo(margin*2, height-margin); + cachedCtx.arcTo(margin, height-margin, margin, height-(margin*2), margin); + + // Left line and top left + cachedCtx.lineTo(margin, margin*2); + cachedCtx.arcTo(margin, margin, margin * 2, margin, margin); + + cachedCtx.closePath(); + cachedCtx.save(); + cachedCtx.shadowOffsetX = 5; + cachedCtx.shadowOffsetY = 5; + cachedCtx.shadowBlur = 4; + cachedCtx.shadowColor = "rgba(30, 30, 30, 0.2)"; + + + var grad = cachedCtx.createLinearGradient(0, margin, 0, height); + grad.addColorStop(0, "rgb(200,200,200)"); + grad.addColorStop(0.4, "rgb(100,100,100)"); + grad.addColorStop(0.5, "rgb(66,66,66)"); + grad.addColorStop(0.7, "rgb(100,100,100)"); + grad.addColorStop(1, "rgb(200,200,200)"); + + cachedCtx.fillStyle = grad; + + cachedCtx.fill(); + + // Redraw so that shadow is seen on all sides + cachedCtx.shadowOffsetX = -5; + cachedCtx.shadowOffsetY = -5; + cachedCtx.fill(); + cachedCtx.restore(); + cachedCtx.fillRect(margin+screenMargin,margin+screenMargin,width-(margin*2)-(screenMargin*2),height-(margin*2)-(screenMargin*2)); + cachedCtx.stroke(); + cachedCtx.restore(); + + var cvs = document.createElement('canvas'); + + /* Gloss */ + var rectX = 0; + var rectY = 0; + var rectWidth = width-(margin+screenMargin)*2; + var rectHeight = height-(margin+screenMargin)*2; + + cvs.width = rectWidth; + cvs.height = rectHeight; + + var ctxBlur = cvs.getContext('2d'); + ctxBlur.beginPath(); + ctxBlur.lineWidth = 1; + ctxBlur.strokeStyle = "#000000"; + ctxBlur.moveTo(rectX,rectY); + ctxBlur.lineTo(rectWidth, rectY); + ctxBlur.lineTo(rectWidth,rectHeight*1.0/3.0); + ctxBlur.bezierCurveTo(rectX+rectWidth / 2.0, rectY + rectHeight * 1.0/3.0, + rectX+rectWidth / 2.0, rectY + rectHeight * 2.0/3.0, + rectX, rectY + rectHeight * 2.0/3.0); + ctxBlur.closePath(); + ctxBlur.fillStyle="rgba(255,255,255,0.3)"; + ctxBlur.fill(); + + stackBlurCanvasRGBA( cvs, 0, 0, rectWidth, rectHeight, 17 ); + + ctx.drawImage(cvs, margin+screenMargin, margin+screenMargin, rectWidth, rectHeight); + + __vboxPreviewCanvasCache[width+'x'+height] = { + 'monitor' : cachedCanvas, + 'gloss' : cvs + }; + + } + + // Draw cached monitor canvas + ctx.drawImage(__vboxPreviewCanvasCache[width+'x'+height]['monitor'], 0, 0, width, height); + + /* Screenshot */ + if(imageObj) { + + ctx.drawImage(imageObj, 0, 0, imageObj.width, imageObj.height, (margin+screenMargin), (margin+screenMargin), width-(margin*2)-(screenMargin*2),height-(margin*2)-(screenMargin*2)); + } + + // Draw cached gloss canvas + ctx.drawImage(__vboxPreviewCanvasCache[width+'x'+height]['gloss'], 0, 0, width, height); + + /* Text */ + if(!imageObj) { + + txtCan = document.createElement('canvas'); + txtCan.width = width-(margin+screenMargin)*2; + txtCan.height = height-(margin+screenMargin)*2; + + fitTextToCanvas(txtCan, text, 18); + + ctx.drawImage(txtCan, (margin+screenMargin), (margin+screenMargin)); + + } + + + return; +} + + +/* HELPERS */ + +/** + * Fit text to a canvas element + * + * @author Ian Moore + */ +var fitTextToCanvas = function(can, text, fontSize) { + + var lineHeightOffset = 1.4; + var lineHeight = fontSize * lineHeightOffset; + var minFontSize = 10; + var padding = 2; + + var words = text.split(" "); + + var maxWidth = can.width; + var maxHeight = can.height; + var context = can.getContext('2d'); + context.moveTo(0,0); + + + var wrapTextLines = function() { + + context.font = "bold " + fontSize + "pt Arial"; + + var line = ''; + var lines = []; + + for (var n = 0; n < words.length; n++) { + + var testLine = line + (line.length ? ' ' : '') + words[n]; + + if((context.measureText(testLine).width + padding) > maxWidth) { + + // Only one word is too big + if(testLine.indexOf(' ') == -1) { + + if(fontSize > minFontSize) { + fontSize *= 0.9; + return wrapTextLines(); + } + line = testLine; + + } else { + lines[lines.length] = line; + line = words[n]; + } + + } else { + + line = testLine; + + } + + } + if(line.length) { + if((context.measureText(line).width + padding) > maxWidth && fontSize > minFontSize) { + fontSize *= 0.9; + return wrapTextLines(); + } + lines[lines.length] = line; + } + return lines; + + }; + + // Initial wrap + lines = wrapTextLines(); + + // Since text will be aligned to the bottom, we subtract + // one lineheight addition because it will be off the + // visible canvas and should not be included in calculations + while(((lines.length * lineHeight)-(lineHeight-fontSize) > maxHeight) && fontSize > minFontSize) { + fontSize *= 0.9; + lines = wrapTextLines(); + lineHeight = fontSize * lineHeightOffset; + } + + context.fillStyle = "#ffffff"; + context.textAlign = "center"; + context.textBaseline = 'alphabetic'; + context.strokeStyle = "#ff0000"; + + var totalHeight = Math.round((lines.length * lineHeight)-(lineHeight-fontSize)); + + for(var i = 0; i < lines.length; i++) { + /* + Uncomment to debug line heights + + context.moveTo(0, (maxHeight/2)-(totalHeight/2)+(lineHeight*i)); + context.lineTo(maxWidth, (maxHeight/2)-(totalHeight/2)+(lineHeight*i)); + context.stroke(); + context.moveTo(0, (maxHeight/2)-(totalHeight/2)+(lineHeight*i)+lineHeight); + context.lineTo(maxWidth, (maxHeight/2)-(totalHeight/2)+(lineHeight*i)+lineHeight); + context.stroke(); + */ + context.fillText(lines[i],(maxWidth/2),(maxHeight/2)-(totalHeight/2)+(lineHeight*i)+lineHeight-(lineHeight-fontSize)); + } + return can; + +}; + +/* + +StackBlur - a fast almost Gaussian Blur For Canvas + +Version: 0.5 +Author: Mario Klingemann +Contact: mario@quasimondo.com +Website: http://www.quasimondo.com/StackBlurForCanvas +Twitter: @quasimondo + +In case you find this class useful - especially in commercial projects - +I am not totally unhappy for a small donation to my PayPal account +mario@quasimondo.de + +Or support me on flattr: +https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript + +Copyright (c) 2010 Mario Klingemann + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +*/ + +var mul_table = [ + 512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512, + 454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512, + 482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456, + 437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512, + 497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328, + 320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456, + 446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335, + 329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512, + 505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405, + 399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328, + 324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271, + 268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456, + 451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388, + 385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335, + 332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292, + 289,287,285,282,280,278,275,273,271,269,267,265,263,261,259]; + + +var shg_table = [ + 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, + 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 ]; + +var stackBlurCanvasRGBA = function( canvas, top_x, top_y, width, height, radius ) +{ + if ( isNaN(radius) || radius < 1 ) return; + radius |= 0; + + //var canvas = document.getElementById( id ); + var context = canvas.getContext("2d"); + var imageData; + + try { + try { + imageData = context.getImageData( top_x, top_y, width, height ); + } catch(e) { + + // NOTE: this part is supposedly only needed if you want to work with local files + // so it might be okay to remove the whole try/catch block and just use + // imageData = context.getImageData( top_x, top_y, width, height ); + try { + netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); + imageData = context.getImageData( top_x, top_y, width, height ); + } catch(e) { + alert("Cannot access local image"); + throw new Error("unable to access local image data: " + e); + return; + } + } + } catch(e) { + alert("Cannot access image"); + throw new Error("unable to access image data: " + e); + } + + var pixels = imageData.data; + + var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum, + r_out_sum, g_out_sum, b_out_sum, a_out_sum, + r_in_sum, g_in_sum, b_in_sum, a_in_sum, + pr, pg, pb, pa, rbs; + + var div = radius + radius + 1; + var widthMinus1 = width - 1; + var heightMinus1 = height - 1; + var radiusPlus1 = radius + 1; + var sumFactor = radiusPlus1 * ( radiusPlus1 + 1 ) / 2; + + var stackStart = new BlurStack(); + var stack = stackStart; + for ( i = 1; i < div; i++ ) + { + stack = stack.next = new BlurStack(); + if ( i == radiusPlus1 ) var stackEnd = stack; + } + stack.next = stackStart; + var stackIn = null; + var stackOut = null; + + yw = yi = 0; + + var mul_sum = mul_table[radius]; + var shg_sum = shg_table[radius]; + + for ( y = 0; y < height; y++ ) + { + r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0; + + r_out_sum = radiusPlus1 * ( pr = pixels[yi] ); + g_out_sum = radiusPlus1 * ( pg = pixels[yi+1] ); + b_out_sum = radiusPlus1 * ( pb = pixels[yi+2] ); + a_out_sum = radiusPlus1 * ( pa = pixels[yi+3] ); + + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + a_sum += sumFactor * pa; + + stack = stackStart; + + for( i = 0; i < radiusPlus1; i++ ) + { + stack.r = pr; + stack.g = pg; + stack.b = pb; + stack.a = pa; + stack = stack.next; + } + + for( i = 1; i < radiusPlus1; i++ ) + { + p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 ); + r_sum += ( stack.r = ( pr = pixels[p])) * ( rbs = radiusPlus1 - i ); + g_sum += ( stack.g = ( pg = pixels[p+1])) * rbs; + b_sum += ( stack.b = ( pb = pixels[p+2])) * rbs; + a_sum += ( stack.a = ( pa = pixels[p+3])) * rbs; + + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + a_in_sum += pa; + + stack = stack.next; + } + + + stackIn = stackStart; + stackOut = stackEnd; + for ( x = 0; x < width; x++ ) + { + pixels[yi+3] = pa = (a_sum * mul_sum) >> shg_sum; + if ( pa != 0 ) + { + pa = 255 / pa; + pixels[yi] = ((r_sum * mul_sum) >> shg_sum) * pa; + pixels[yi+1] = ((g_sum * mul_sum) >> shg_sum) * pa; + pixels[yi+2] = ((b_sum * mul_sum) >> shg_sum) * pa; + } else { + pixels[yi] = pixels[yi+1] = pixels[yi+2] = 0; + } + + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + a_sum -= a_out_sum; + + r_out_sum -= stackIn.r; + g_out_sum -= stackIn.g; + b_out_sum -= stackIn.b; + a_out_sum -= stackIn.a; + + p = ( yw + ( ( p = x + radius + 1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2; + + r_in_sum += ( stackIn.r = pixels[p]); + g_in_sum += ( stackIn.g = pixels[p+1]); + b_in_sum += ( stackIn.b = pixels[p+2]); + a_in_sum += ( stackIn.a = pixels[p+3]); + + r_sum += r_in_sum; + g_sum += g_in_sum; + b_sum += b_in_sum; + a_sum += a_in_sum; + + stackIn = stackIn.next; + + r_out_sum += ( pr = stackOut.r ); + g_out_sum += ( pg = stackOut.g ); + b_out_sum += ( pb = stackOut.b ); + a_out_sum += ( pa = stackOut.a ); + + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + a_in_sum -= pa; + + stackOut = stackOut.next; + + yi += 4; + } + yw += width; + } + + + for ( x = 0; x < width; x++ ) + { + g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0; + + yi = x << 2; + r_out_sum = radiusPlus1 * ( pr = pixels[yi]); + g_out_sum = radiusPlus1 * ( pg = pixels[yi+1]); + b_out_sum = radiusPlus1 * ( pb = pixels[yi+2]); + a_out_sum = radiusPlus1 * ( pa = pixels[yi+3]); + + r_sum += sumFactor * pr; + g_sum += sumFactor * pg; + b_sum += sumFactor * pb; + a_sum += sumFactor * pa; + + stack = stackStart; + + for( i = 0; i < radiusPlus1; i++ ) + { + stack.r = pr; + stack.g = pg; + stack.b = pb; + stack.a = pa; + stack = stack.next; + } + + yp = width; + + for( i = 1; i <= radius; i++ ) + { + yi = ( yp + x ) << 2; + + r_sum += ( stack.r = ( pr = pixels[yi])) * ( rbs = radiusPlus1 - i ); + g_sum += ( stack.g = ( pg = pixels[yi+1])) * rbs; + b_sum += ( stack.b = ( pb = pixels[yi+2])) * rbs; + a_sum += ( stack.a = ( pa = pixels[yi+3])) * rbs; + + r_in_sum += pr; + g_in_sum += pg; + b_in_sum += pb; + a_in_sum += pa; + + stack = stack.next; + + if( i < heightMinus1 ) + { + yp += width; + } + } + + yi = x; + stackIn = stackStart; + stackOut = stackEnd; + for ( y = 0; y < height; y++ ) + { + p = yi << 2; + pixels[p+3] = pa = (a_sum * mul_sum) >> shg_sum; + if ( pa > 0 ) + { + pa = 255 / pa; + pixels[p] = ((r_sum * mul_sum) >> shg_sum ) * pa; + pixels[p+1] = ((g_sum * mul_sum) >> shg_sum ) * pa; + pixels[p+2] = ((b_sum * mul_sum) >> shg_sum ) * pa; + } else { + pixels[p] = pixels[p+1] = pixels[p+2] = 0; + } + + r_sum -= r_out_sum; + g_sum -= g_out_sum; + b_sum -= b_out_sum; + a_sum -= a_out_sum; + + r_out_sum -= stackIn.r; + g_out_sum -= stackIn.g; + b_out_sum -= stackIn.b; + a_out_sum -= stackIn.a; + + p = ( x + (( ( p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2; + + r_sum += ( r_in_sum += ( stackIn.r = pixels[p])); + g_sum += ( g_in_sum += ( stackIn.g = pixels[p+1])); + b_sum += ( b_in_sum += ( stackIn.b = pixels[p+2])); + a_sum += ( a_in_sum += ( stackIn.a = pixels[p+3])); + + stackIn = stackIn.next; + + r_out_sum += ( pr = stackOut.r ); + g_out_sum += ( pg = stackOut.g ); + b_out_sum += ( pb = stackOut.b ); + a_out_sum += ( pa = stackOut.a ); + + r_in_sum -= pr; + g_in_sum -= pg; + b_in_sum -= pb; + a_in_sum -= pa; + + stackOut = stackOut.next; + + yi += width; + } + } + + context.putImageData( imageData, top_x, top_y ); + +}; + + + +var BlurStack = function() +{ + this.r = 0; + this.g = 0; + this.b = 0; + this.a = 0; + this.next = null; +}; diff --git a/js/chooser.js b/js/chooser.js index 41f24bc..1bfa27b 100644 --- a/js/chooser.js +++ b/js/chooser.js @@ -1,2451 +1,2451 @@ -/**
- *
- * @fileOverview Chooser (vm list) singleton. Provides vboxChooser
- * @author Ian Moore (imoore76 at yahoo dot com)
- * @version $Id: chooser.js 591 2015-04-11 22:40:47Z imoore76 $
- * @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com)
- *
- */
-
-/**
- * Chooser selection mode constants
- */
-var vboxSelectionModeNone = 0;
-var vboxSelectionModeSingleVM = 1;
-var vboxSelectionModeMultiVM = 2;
-var vboxSelectionModeSingleGroup = 3;
-
-
-/**
- * @namespace vboxChooser
- *
- * Draws machine selection chooser and controls selection list
- * @see js/eventlistener.js
- */
-var vboxChooser = {
-
- // VM list
- vms : {},
-
- // VM tool tip
- _vmToolTip : '<nobr>%1<br></nobr><nobr>%2 since %3</nobr><br><nobr>Session %4</nobr>',
-
- // Anchor element
- _anchorid : null,
- _anchor : null,
-
- /* Internal list of all unique selected items */
- _selectedList : [],
-
- /* List of unique selected VMs */
- selectedVMs : [],
-
- /* Holds group definitions */
- _groupDefs : [],
-
- /* selection mode can be
-
- var vboxSelectionModeNone = 0,
- var vboxSelectionModeSingleVM = 1,
- var vboxSelectionModeMultiVM = 2,
- var vboxSelectionModeSingleGroup = 3,
- */
- selectionMode : vboxSelectionModeNone,
-
- /* Check phpVirtualBox version and VirtualBox
- * version compatibility.
- */
- _versionChecked : false,
-
- /* Some items are not editable while vmGroup
- * definitions are being written
- */
- _editable : true,
-
- /* Context menus */
- _vmContextMenuObj : null,
- _vmGroupContextMenuObj : null,
-
- /* Holds history of showing only single groups */
- _showOnlyGroupHistory : [],
-
- /* Group definition extra value key */
- _groupDefinitionKey : '',
-
- /* Whether chooser is in compact mode or not */
- _compact : false,
-
- /**
- * Set anchor id to draw to
- */
- setAnchorId : function(aid) {
- vboxChooser._anchorid = aid;
- vboxChooser._anchor = $('#'+aid);
-
- vboxChooser._anchor.html("<div id='vboxChooserSpinner' style='text-align: center'><div><img src='images/spinner.gif' /></div></div>");
-
- vboxChooser._anchor.hover(function(){
- $(this).addClass('vboxChooserDropTargetHoverRoot');
- },function() {
- $(this).removeClass('vboxChooserDropTargetHoverRoot');
- });
-
- $(window).resize(function(){
-
- // Get anchor id and add / remove class
- var w = parseInt($(vboxChooser._anchor).innerWidth());
- if(w < 120) {
- $(vboxChooser._anchor).addClass('vboxChooserMini');
- vboxChooser._compact = true;
- } else {
- $(vboxChooser._anchor).removeClass('vboxChooserMini');
- vboxChooser._compact = false;
- }
-
- vboxChooser._resizeElements(true);
-
- });
- },
-
- /**
- * Set context menus
- *
- */
- setContextMenu : function(target, menuitems) {
-
- switch(target) {
-
- // Group menu
- case 'group':
- vboxChooser._vmGroupContextMenuObj = new vboxMenu({'name': vboxChooser._anchorid+'vmgroups',
- 'menuItems': menuitems,
- 'language_context': 'UIActionPool'});
- vboxChooser._vmGroupContextMenuObj.update();
- break;
-
- // VM Menu
- case 'vm':
- vboxChooser._vmContextMenuObj = new vboxMenu({'name': vboxChooser._anchorid+'vms',
- 'menuItems': menuitems,
- 'language_context': 'UIActionPool'});
- vboxChooser._vmContextMenuObj.update();
- break;
-
- // Main list menu
- case 'anchor':
-
- var vboxChooserPaneMenu = new vboxMenu({'name': vboxChooser._anchorid+'Pane',
- 'menuItems': menuitems,
- 'language_context': 'UIActionPool'});
- $('#'+vboxChooser._anchorid).parent().contextMenu({
- menu: vboxChooserPaneMenu.menuId()
- },
- vboxChooserPaneMenu.menuClickCallback
- );
-
- break;
-
- default:
- vboxAlert('vboxChooser::setContextMenu: unknown context menu type (' + target + ')');
- }
- },
-
- /*
- * Return true if a selected VM is in the given state
- */
- isSelectedInState : function(state) {
-
- for(var i = 0; i < vboxChooser.selectedVMs.length; i++) {
- if(vboxVMStates['is'+state](vboxVMDataMediator.getVMData(vboxChooser.selectedVMs[i])))
- return true;
- }
- return false;
-
- },
-
- /**
- * Return true if the passed VM is selected
- */
- isVMSelected : function(vmid) {
- return (jQuery.inArray(vmid,vboxChooser.selectedVMs) > -1);
- },
-
- /**
- * Return selected VM data in array
- */
- getSelectedVMsData : function() {
-
- var vms = [];
- for(var i = 0; i < vboxChooser.selectedVMs.length; i++) {
- vms[vms.length] = vboxVMDataMediator.getVMData(vboxChooser.selectedVMs[i]);
- }
- return vms;
- },
-
- /**
- * Triggered when selection list has changed
- */
- selectionListChanged : function(selectionList) {
-
- if(!selectionList) selectionList = [];
-
- selectionMode = vboxSelectionModeNone;
-
- // Hold unique selected VMs
- var vmListUnique = {};
- for(var i = 0; i < selectionList.length; i++) {
- if(selectionList[i].type == 'group') {
- vboxChooser.getGroupElement(selectionList[i].groupPath, true).find('table.vboxChooserVM:not(.ui-draggable-dragging)').each(function(idx,elm){
- if(elm) {
- var vmid = $(elm).data('vmid');
- if(vmid)
- vmListUnique[vmid] = vmid;
- }
- });
- switch(selectionMode) {
- case vboxSelectionModeSingleGroup:
- case vboxSelectionModeSingleVM:
- selectionMode = vboxSelectionModeMultiVM;
- break;
- case vboxSelectionModeNone:
- selectionMode = vboxSelectionModeSingleGroup;
- }
- } else {
- switch(selectionMode) {
- case vboxSelectionModeNone:
- selectionMode = vboxSelectionModeSingleVM;
- break;
- default:
- selectionMode = vboxSelectionModeMultiVM;
- }
-
- vmListUnique[selectionList[i].id] = selectionList[i].id;
- }
- }
-
- // Change selection list
- var selectedVMs = [];
- for(var i in vmListUnique) {
- selectedVMs[selectedVMs.length] = i;
- }
-
- vboxChooser.selectedVMs = selectedVMs;
-
- // If there is only one unique vm selected,
- // selection mode becomes single VM if the
- // current selection mode is not singleGroup
- if(vboxChooser.selectedVMs.length == 1 && selectionMode != vboxSelectionModeSingleGroup)
- selectionMode = vboxSelectionModeSingleVM;
-
- vboxChooser.selectionMode = selectionMode;
-
- vboxChooser._selectedList = selectionList;
-
- $('#vboxPane').trigger('vmSelectionListChanged',[vboxChooser]);
-
-
- },
-
- /**
- * Return the single selected VM's id if
- * only one vm is selected. Else null.
- */
- getSingleSelectedId : function() {
- if(vboxChooser.selectedVMs.length == 1) {
- return vboxChooser.selectedVMs[0];
- }
- return null;
- },
-
- /*
- * Return a single vm if only one is selected.
- * Else null.
- */
- getSingleSelected : function() {
- if(vboxChooser.selectedVMs.length == 1) {
- return vboxVMDataMediator.getVMData(vboxChooser.selectedVMs[0]);
- }
- return null;
- },
-
- /*
- * Update list of VMs from data received
- * from ajax query
- */
- updateList : function(vmlist) {
-
- // We were stopped before the request returned data
- if(!vboxChooser._running) return;
-
-
- // No list? Something is wrong
- if(!vmlist) {
-
- phpVirtualBoxFailure();
-
- vboxChooser.stop();
- vboxChooser._anchor.children().remove();
- return;
- }
-
- // Remove spinner
- vboxChooser._anchor.children().remove();
-
- // Render host
- vboxChooser._anchor.append(vboxChooser.vmHTML(
- {
- 'id':'host',
- 'state':'Hosting',
- 'owner':'',
- 'name':$('#vboxPane').data('vboxConfig').name,
- 'OSTypeId':'VirtualBox_Host'
- }
- ));
-
- // Render root group
- vboxChooser._anchor.append(vboxChooser.groupHTML("/"));
-
- // Enforce VM ownership
- if($('#vboxPane').data('vboxConfig').enforceVMOwnership && !$('#vboxPane').data('vboxSession').admin) {
- vmlist = jQuery.grep(vmlist,function(vm,i){
- return (vm.owner == $('#vboxPane').data('vboxSession').user);
- });
- }
-
- var groups = [];
- // Each item in list
- for(var i = 0; i < vmlist.length; i++) {
- // Update
- vboxChooser.updateVMElement(vmlist[i], true);
- groups = groups.concat(vmlist[i].groups);
- }
-
- // Sort groups
- var groupsSorted = {};
- for(var i = 0; i < groups.length; i++) {
- if(groupsSorted[groups[i]]) continue;
- groupsSorted[groups[i]] = true;
- var gElm = vboxChooser.getGroupElement(groups[i], true);
- if(gElm[0]) vboxChooser.sortGroup(gElm);
- }
-
- // compose group definitions
- vboxChooser.composeGroupDef();
-
- // Set initial resize
- vboxChooser._initialResize = true;
- vboxChooser._resizeElements(true);
-
- },
-
- /*
- * Save collapsed group list
- */
- _collapsedGroups : [],
- _saveCollapsedGroups : function(){
-
- // Write out collapsed group list
- var cGroupList = [];
- vboxChooser._anchor.find('div.vboxVMGroupCollapsed:not(.ui-draggable-dragging)').each(function(idx,elm) {
- cGroupList[cGroupList.length] = $(elm).data('vmGroupPath');
- });
-
- var groupListKey = $('#vboxPane').data('vboxConfig').key+'-collapsedGroups';
- vboxSetLocalDataItem(groupListKey, cGroupList.join(','), true);
-
- // Cache instead of using local storage
- vboxChooser._collapsedGroups = cGroupList;
- },
-
- /*
- * Return true if group is collapsed
- */
- _isGroupCollapsed : function(gpath) {
- return(jQuery.inArray(gpath,vboxChooser._collapsedGroups) > -1);
- },
-
- /*
- * Resize group and VM titles
- */
- _scrollbarWidth : 0,
- _scrollbarWasVisible: false,
- _initialResize: false,
- _resizeElements : function(forceResize) {
-
- // Haven't completed our initial resizing yet
- if(!vboxChooser._initialResize) {
- return;
- }
-
- var sbVisible = (vboxChooser._anchor.get(0).scrollHeight > vboxChooser._anchor.height());
-
- // Nothing changed since resize
- if(!forceResize && (sbVisible == vboxChooser._scrollbarWasVisible)) {
- return;
- }
-
- vboxChooser._scrollbarWasVisible = sbVisible;
-
- var groupTitleWidth = vboxChooser._anchor.width() - (vboxChooser._compact ? 22 : 32) - (sbVisible ? vboxChooser._scrollbarWidth : 0);
- var vmTitleWidth = groupTitleWidth - (vboxChooser._compact ? -12 : 18); // (2px padding on .vboxChooserGroupVMs +
- // 2px border on table + 4px margin on icon) * 2
- var groupLevelOffset = (vboxChooser._compact ? 8 : 8); // (2px margin + 2px border) * 2
-
-
- // Now that we have sizes, we can inject styles
- $('#vboxChooserStyle').empty().remove();
-
- var styleRules = [];
- var path = ['div.vboxChooserGroupRootLevel'];
-
- // Special case for root level VM list
- styleRules[styleRules.length] = 'div.vboxChooserGroupRootLevel > div.vboxChooserGroupVMs table.vboxChooserVM div.vboxFitToContainer { width: ' + vmTitleWidth + 'px; }';
-
- // Special case for group header when only showing one group
- styleRules[styleRules.length] = 'div.vboxChooserGroupShowOnly.vboxChooserGroupRootLevel > div.vboxChooserGroupHeader span.vboxChooserGroupName { max-width: ' + (groupTitleWidth - 4) + 'px; }';
-
- // Bottom group resize bars
- styleRules[styleRules.length] = 'div.vboxChooserGroupRootLevel > div.vboxChooserDropTargetBottom { width: ' + (groupTitleWidth) + 30 + 'px; }';
-
- for(var i = 1; i < 11; i++) {
-
- // Group titles at this level
- styleRules[styleRules.length] = path.join(' > ') + ' > div.vboxChooserGroup > div.vboxChooserGroupHeader span.vboxChooserGroupName { max-width: ' + (groupTitleWidth - (i*groupLevelOffset)) + 'px; }';
-
- // VM titles at this level
- styleRules[styleRules.length] = path.join(' > ') + ' > div.vboxChooserGroup > div.vboxChooserGroupVMs table.vboxChooserVM div.vboxFitToContainer { width: ' + (vmTitleWidth - (i*(groupLevelOffset))) + 'px; }';
-
- // Bottom group resize bars
- styleRules[styleRules.length] = path.join(' > ') +' > div.vboxChooserGroup > div.vboxChooserDropTargetBottom { width: ' + (groupTitleWidth + 30 - (i*groupLevelOffset)) + 'px; }';
-
- path[path.length] = 'div.vboxChooserGroup';
- }
-
- // Style for minified vmlist
- if(vboxChooser._compact) {
- // Title moves left
- styleRules[styleRules.length] = 'div.vboxChooserGroup > div.vboxChooserGroupVMs table.vboxChooserVM div.vboxVMName { position: relative; left: -20px; }';
- // Icon moves down
- styleRules[styleRules.length] = 'div.vboxChooserGroup > div.vboxChooserGroupVMs table.vboxChooserVM img.vboxVMIcon { position: relative; top: 8px; }';
- // State text goes away
- styleRules[styleRules.length] = 'div.vboxChooserGroup > div.vboxChooserGroupVMs table.vboxChooserVM span.vboxVMState { display: none; }';
- // Less padding
- styleRules[styleRules.length] = 'div.vboxChooserGroup > div.vboxChooserGroupVMs table.vboxChooserVM td { padding: 0px; }';
- // Some group header items and drop targets go away
- styleRules[styleRules.length] = 'div.vboxChooserGroup > div.vboxChooserGroupHeader > .vboxChooserGroupNameArrowCollapse, #' +vboxChooser._anchorid + ' div.vboxChooserGroup .vboxChooserDropTarget { display: none; }';
- styleRules[styleRules.length] = 'div.vboxChooserGroup { overflow: hidden; }';
- // host
- styleRules[styleRules.length] = '#vboxChooserVMHost .vboxVMState { display: none; }';
- // group header
- styleRules[styleRules.length] = 'div.vboxChooserGroup div.vboxChooserGroupHeader { height: auto; padding: 2px; }';
-
- }
- $('head').append('<style type="text/css" id="vboxChooserStyle">#'+vboxChooser._anchorid + ' ' + styleRules.join("\n#"+vboxChooser._anchorid + " ") + '</style>');
-
- },
-
- /*
- * Get group element by path
- */
- getGroupElement : function(gpath, noCreate) {
-
- if(!gpath) gpath = '/';
- var gnames = gpath.split('/');
- var groot = vboxChooser._anchor.children('div.vboxChooserGroup:not(.ui-draggable-dragging)');
- for(var i = 1; i < gnames.length; i++) {
-
- if(!gnames[i]) continue;
-
- var group = groot.children('div.vboxChooserGroup:not(.ui-draggable-dragging)').children('div.vboxChooserGroupIdentifier[title="'+gnames[i]+'"]').parent();
-
- // If it does not exist, create it
- if(!group[0]) {
-
- if(noCreate) return null;
-
- var gpath = '/';
- for(var a = 1; a <= i; a++) {
- gpath = gpath + '/' + gnames[a];
- }
- gpath = gpath.replace('//','/');
-
- vboxChooser.groupHTML(gpath).insertBefore(groot.children('div.vboxChooserGroupVMs'));
-
- vboxChooser.sortGroup(groot);
-
- // Resize chooser elements
- vboxChooser._initialResize = true;
- vboxChooser._resizeElements();
-
- groot = groot.children('div.vboxChooserGroup:not(.ui-draggable-dragging)').children('div.vboxChooserGroupIdentifier[title="'+gnames[i]+'"]').parent();
-
- } else {
- groot = group;
- }
-
- }
- return groot;
- },
-
- /*
- *
- * Update VM elements
- *
- */
- updateVMElement : function(vmUpdate, newVM) {
-
- // Not running.. don't do anything
- if(!vboxChooser._running) return;
-
- // Stale event after vm was removed
- if(!vmUpdate) return;
-
- // New VM
- if(newVM) {
-
- // New VM.. add it to groups..
- if(!vmUpdate.groups || vmUpdate.groups.length == 0)
- vmUpdate.groups = ['/'];
-
- for(var i = 0; i < vmUpdate.groups.length; i++) {
- var gElm = $(vboxChooser.getGroupElement(vmUpdate.groups[i]));
- vboxChooser.vmHTML(vmUpdate).appendTo(
- gElm.children('div.vboxChooserGroupVMs')
- );
- }
-
- // Existing VM. Replace existing elements
- } else {
-
- $('#'+vboxChooser._anchorid).find('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmUpdate.id).each(function(i,elm){
-
- var newHTML = vboxChooser.vmHTML(vmUpdate);
- if($(elm).hasClass('vboxListItemSelected')) {
- $(newHTML).addClass('vboxListItemSelected').removeClass('vboxHover');
- }
- $(elm).children().replaceWith(newHTML.children());
- });
-
- }
-
- },
-
-
- /*
- * Returns true if there are VMs with ID vmid that are not selected
- */
- vmHasUnselectedCopy : function (vmid) {
- return ($(vboxChooser._anchor).find('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmid+':not(.vboxListItemSelected)').length > 0);
- },
-
- /*
- * Remove selected VMs from the list and rewrite group definitions
- * this assumes that there are other copies of these VMs that are not
- * selected.
- */
- removeVMs : function(vmids) {
-
- for(var i = 0; i < vmids.length; i++) {
- $(vboxChooser._anchor).find('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmids[i]+'.vboxListItemSelected').remove();
- }
-
- // Update selection list
- vboxChooser._selectedList = vboxChooser._selectedList.filter(function(v){
- return (v.type == 'group' || (jQuery.inArray(v.id, vmids) == -1));
- });
-
- // Tell interface that selection list has changed
- vboxChooser.selectionListChanged(vboxChooser._selectedList);
-
- // compose and save group definitions
- vboxChooser.composeGroupDef(true);
-
-
- // Possible resize needed
- vboxChooser._resizeElements(true);
-
- },
-
- /*
- * Generate HTML from VM definition
- */
- vmHTML : function (vmn) {
-
- var tbl = $('<table />').attr({'class':'vboxChooserItem-'+vboxChooser._anchorid+'-'+vmn.id + " vboxChooserVM"})
- .on('mousedown',vboxChooser.selectItem)
- .hoverClass('vboxHover').data('vmid',vmn.id);
-
-
- // Drag-and-drop functionality
- /////////////////////////////////
- if(vmn.id != 'host' && $('#vboxPane').data('vboxSession').admin) {
-
- $(tbl).draggable({'cursorAt':{left: -10, top: -10},'helper':function(){
- return $(this).clone().css({'width':($(this).width()+2)+'px','display':'inline','background':'#fff','border-color':'#69f'}).removeClass('vboxHover');
-
- // drag start
- },'start':function(e) {
-
- if(!vboxChooser._editable) return false;
-
- $(vboxChooser._anchor).disableSelection();
- vboxChooser._dragging = vmn.id;
- $(vboxChooser._anchor).find('table.vboxHover').removeClass('vboxHover');
-
- // drag stop
- },'stop':function(e) {
- vboxChooser.vmDropped(e, $(this));
- }});
- }
-
- // Functionality to drop above / below VM
- /////////////////////////////////////////////
- var td = $('<td />').attr({'colspan':'2'}).addClass('vboxChooserDropTarget vboxDropTargetTop');
- if(vmn.id != 'host') {
- td.hover(function(){
- if(vboxChooser._dragging && vboxChooser._dragging != vmn.id)
- $(this).addClass('vboxChooserDropTargetHover');
- },function(){
- $(this).removeClass('vboxChooserDropTargetHover');
- }
- );
- }
- $('<tr />').append(td).appendTo(tbl);
-
-
-
- // VM OS type icon
- var tr = $('<tr />');
- if($('#vboxPane').data('vboxConfig').enableCustomIcons && vmn.customIcon) {
- $('<td />').attr({'rowspan':'2'}).html("<img src='" + vmn.customIcon + "' class='vboxVMIcon' />").appendTo(tr);
- } else {
- $('<td />').attr({'rowspan':'2'}).html("<img src='images/vbox/" + vboxGuestOSTypeIcon(vmn.OSTypeId) + "' class='vboxVMIcon" + (vmn.id == 'host' ? " vboxHostIcon" : "") + "' />").appendTo(tr);
- }
-
-
- // VM Name
- var td = $('<td />').attr({'class':'vboxVMTitle'});
-
- // Host will have HTML in name and unique id
- if(vmn.id == 'host') {
-
- $(tbl).attr('id', 'vboxChooserVMHost');
-
- // Check for multiple server config
- if($('#vboxPane').data('vboxConfig').servers.length) {
-
- // If there are multiple servers configured, setup menu
- if(!$('#vboxServerMenu')[0]) {
- var servers = $('#vboxPane').data('vboxConfig').servers;
- var ul = $('<ul />').attr({'id':'vboxServerMenu','style':'display: none','class':'contextMenu'});
- for(var i = 0; i < servers.length; i++) {
- $('<li />').html("<a href='#" + $('<div />').html(servers[i].name).text() + "' style='background-image: url(images/vbox/OSE/VirtualBox_16px.png);'>"+$('<div />').html(servers[i].name).text()+"</a>").appendTo(ul);
- }
- $('#vboxPane').append(ul);
- }
-
- var span = $('<span />').attr({'class':'vboxServerLink'}).text('('+$('#vboxPane').data('vboxConfig').name+')').contextMenu({
- menu: 'vboxServerMenu',
- button: 0,
- mode: 'menu'
- },
- function(a) {
-
- if(a == $('#vboxPane').data('vboxConfig').name) return;
-
- // Show loading screen
- var l = new vboxLoader();
- l.showLoading();
-
- // Empty selection list
- vboxChooser.selectionListChanged();
-
-
- // Unsubscribe from events
- $.when(vboxEventListener.stop()).done(function() {
-
- // Expire data mediator data
- vboxVMDataMediator.expireAll();
-
- // Trigger host change
- vboxSetCookie("vboxServer",a);
- $('#vboxPane').trigger('hostChange',[a]);
-
- }).always(function(){
-
- // remove loading screen
- l.removeLoading();
-
- });
-
-
- }
- );
- $(td).html('<span class="vboxVMName">VirtualBox</span> ').append(span);
- } else {
- $(td).html('<span class="vboxVMName">VirtualBox</span> ('+vmn.name+')');
- }
-
- // Not rendering host
- } else {
-
- $(td).append('<div class="vboxFitToContainer vboxVMName"><span class="vboxVMName">'+$('<span />').text(vmn.name).html()+'</span>'+ (vmn.currentSnapshotName ? '<span class="vboxVMChooserSnapshotName"> (' + $('<span />').text(vmn.currentSnapshotName).html() + ')</span>' : '')+'</div>');
-
-
- // Table gets tool tips
- tip = trans(vboxChooser._vmToolTip, 'UIVMListView').replace('%1',('<b>'+$('<span />')
- .text(vmn.name).html()+'</b>'+(vmn.currentSnapshotName ? ' (' + $('<span />')
- .text(vmn.currentSnapshotName).html() + ')' : '')))
- .replace('%2',trans(vboxVMStates.convert(vmn.state),'VBoxGlobal'))
- .replace('%3',vboxDateTimeString(vmn.lastStateChange))
- .replace('%4',trans(vmn.sessionState,'VBoxGlobal').toLowerCase());
-
- $(tbl).tipped({'source':tip,'position':'mouse','delay':1500});
- }
-
- $(tr).append(td).appendTo(tbl);
-
- // VM state row
- var tr = $('<tr />');
- var td = $('<td />').attr({'class':(vmn.id != 'host' && vmn.sessionState != 'Unlocked' ? 'vboxVMSessionOpen' : '')});
-
- // Add VirtualBox version if hosting
- if(vmn.id == 'host') {
-
- $(td).html("<div class='vboxFitToContainer vboxVMState'><img src='images/vbox/" + vboxMachineStateIcon(vmn.state) +"' /><span class='vboxVMState'>" + trans(vboxVMStates.convert(vmn.state),'VBoxGlobal') + ' - ' + $('#vboxPane').data('vboxConfig').version.string+'</span></div>');
-
- // Check for version mismatches?
- if(!vboxChooser._versionChecked) {
- vboxChooser._versionChecked = true;
- var vStr = $('#vboxPane').data('vboxConfig').phpvboxver.substring(0,$('#vboxPane').data('vboxConfig').phpvboxver.indexOf('-'));
- var vers = $('#vboxPane').data('vboxConfig').version.string.replace('_OSE','').split('.');
- if(vers[0]+'.'+vers[1] != vStr) {
- vboxAlert('This version of phpVirtualBox ('+$('#vboxPane').data('vboxConfig').phpvboxver+') is incompatible with VirtualBox ' + $('#vboxPane').data('vboxConfig').version.string + ". You probably need to <a href='http://sourceforge.net/projects/phpvirtualbox/files/' target=_blank>download the latest phpVirtualBox " + vers[0]+'.'+vers[1] + "-x</a>.<p>See the Versioning section below the file list in the link for more information</p>",{'width':'auto'});
- }
- }
- } else {
- $(td).html("<div class='vboxFitToContainer vboxVMState'><img src='images/vbox/" + vboxMachineStateIcon(vmn.state) +"' /><span class='vboxVMState'>" + trans(vboxVMStates.convert(vmn.state),'VBoxGlobal') + '</span></div>');
- }
-
- $(tr).append(td).appendTo(tbl);
-
- // Droppable targets
- var td = $('<td />').attr({'colspan':'2'}).addClass('vboxChooserDropTarget vboxDropTargetBottom');
- if(vmn.id != 'host') {
- td.hover(function(){
- if(vboxChooser._dragging && vboxChooser._dragging != vmn.id)
- $(this).addClass('vboxChooserDropTargetHover');
- },function(){
- $(this).removeClass('vboxChooserDropTargetHover');
- }
- );
- }
- $('<tr />').addClass('vboxChooserDropTarget').css({'height':'4px'}).append(td).appendTo(tbl);
-
-
- // Context menus?
- if(vboxChooser._vmContextMenuObj) {
-
- $(tbl).contextMenu({
- menu: vboxChooser._vmContextMenuObj.menuId(),
- menusetup : function(el) {
- if(!$(el).hasClass('vboxListItemSelected')) $(el).trigger('click');
- }
- },function(act,el,pos,d,e){
- vboxChooser._vmContextMenuObj.menuClickCallback(act);
- });
-
- // Open settings on dblclick
- $(tbl).dblclick(function(){
- if(vboxChooser._vmContextMenuObj.menuItems['settings'].enabled())
- vboxChooser._vmContextMenuObj.menuItems['settings'].click();
- });
- }
-
- return tbl;
-
- },
-
-
- /*
- * VM Group Dropped
- */
- vmGroupDropped : function(e, droppedGroup) {
-
-
- $(vboxChooser._anchor).enableSelection();
-
-
- var vmGroupPath = vboxChooser._draggingGroup;
- vboxChooser._draggingGroup = false;
- $(droppedGroup).removeClass('vboxHover');
-
- if(!vboxChooser._editable) return false;
-
- // Cannot drag a group that contains a VM without
- // an unlocked session state if it will modify VM
- // Groups
- var sessionLocked = false;
- if($(droppedGroup).find('td.vboxVMSessionOpen')[0])
- sessionLocked=true;
-
-
- // Check for above/below group first
- var dropTarget = vboxChooser._anchor.find('div.vboxChooserDropTargetHover').first();
- if(dropTarget[0]) {
-
- // Make sure that this wasn't dropped onto a sub-group or itself
- if(
- !dropTarget.closest('div.vboxChooserGroup')[0]
- ||
- vmGroupPath == dropTarget.closest('div.vboxChooserGroup').data('vmGroupPath')
- ||
- dropTarget.closest('div.vboxChooserGroup').data('vmGroupPath').indexOf(vmGroupPath + '/') == 0
- ) {
- return;
- }
-
-
- // If we are not still in the same group, check for name conflict
- var currParentGroupPath = $(droppedGroup).closest('div.vboxChooserGroup').parent().closest('div.vboxChooserGroup').data('vmGroupPath');
-
- if(dropTarget.closest('div.vboxChooserGroup').parent().closest('div.vboxChooserGroup').data('vmGroupPath') != currParentGroupPath) {
-
- // Do not allow to be dragged into another group
- // if there is a Vm with a locked session in this one
- if(sessionLocked && !$('#vboxPane').data('vboxConfig')['phpVboxGroups']) return;
-
- // Make sure there are no conflicts
- var groupName = $(droppedGroup).children('div.vboxChooserGroupIdentifier').attr('title');
- var newGroupName = groupName;
-
-
- var i = 2;
- while(vboxChooser.groupNameConflicts(dropTarget.closest('div.vboxChooserGroup').parent(), newGroupName)) {
- newGroupName = groupName + ' (' + (i++) + ')';
- }
-
- $(droppedGroup).children('div.vboxChooserGroupIdentifier').attr({'title':newGroupName})
- .siblings('div.vboxChooserGroupHeader')
- .children('span.vboxChooserGroupName').text(newGroupName);
-
- }
-
- // Insert before or insert after?
- if(dropTarget.hasClass('vboxDropTargetTop')) {
- $(droppedGroup).detach().insertBefore(dropTarget.closest('div.vboxChooserGroup'));
- } else {
- $(droppedGroup).detach().insertAfter(dropTarget.closest('div.vboxChooserGroup'));
- }
-
-
- // Dropped onto a group or main VM list
- } else {
-
- // Will not do this if this group contains
- // a VM with a locked session
- if(sessionLocked && !$('#vboxPane').data('vboxConfig')['phpVboxGroups']) return;
-
- var dropTarget = vboxChooser._anchor.find('div.vboxHover').first();
-
-
- // Dropped onto a group
- if(dropTarget[0] && dropTarget.parent().hasClass('vboxChooserGroup')) {
-
- dropTarget = dropTarget.parent();
-
- // Make sure that this wasn't dropped onto a sub-group or itself
- if(
- vmGroupPath == dropTarget.data('vmGroupPath')
- ||
- dropTarget.closest('div.vboxChooserGroup').data('vmGroupPath').indexOf(vmGroupPath + '/') == 0
- ) {
- return;
- }
-
- // Dropped onto main vm list
- } else if($(vboxChooser._anchor).find('div.vboxGroupHover').length == 0 && $(vboxChooser._anchor).hasClass('vboxChooserDropTargetHoverRoot')) {
-
- dropTarget = null;
-
- // Only showing one group?
- if(vboxChooser._showOnlyGroupHistory.length > 0) {
- dropTarget = $(vboxChooser._showOnlyGroupHistory[vboxChooser._showOnlyGroupHistory.length-1]);
- }
-
- if(!$(dropTarget)[0])
- dropTarget = vboxChooser._anchor.children('div.vboxChooserGroup');
-
- } else {
- return;
- }
-
- // Make sure there are no conflicts
- var newElm = $(droppedGroup).detach();
- var groupName = $(droppedGroup).children('div.vboxChooserGroupIdentifier').attr('title');
- var newGroupName = groupName;
-
- var i = 2;
- while(vboxChooser.groupNameConflicts(dropTarget, newGroupName, $(newElm).data('vmGroupPath'))) {
- newGroupName = groupName + ' (' + (i++) + ')';
- }
-
- $(newElm)
- .children('div.vboxChooserGroupIdentifier').attr({'title':newGroupName})
- .siblings('div.vboxChooserGroupHeader')
- .children('span.vboxChooserGroupName').text(newGroupName);
- $(newElm).insertBefore(dropTarget.children('div.vboxChooserGroupVMs'));
-
- }
-
- // vmGroup dropped - compose and save group definitions
- vboxChooser.composeGroupDef(true);
-
- // Hide group info
- vboxChooser._anchor.find('div.vboxChooserGroupHeader').trigger('mouseout');
-
- // Resize chooser elements
- vboxChooser._resizeElements();
-
- vboxChooser.selectionListChanged(vboxChooser._selectedList);
-
- },
-
- /*
- * VM dropped
- */
- vmDropped : function (e, droppedVM){
-
-
- $(vboxChooser._anchor).enableSelection();
- vboxChooser._dragging = null;
-
- if(!vboxChooser._editable) return false;
-
- // Cannot drag if this VM's session is not open
- var thisSessionLocked = false;
- var vmData = vboxVMDataMediator.getVMData($(droppedVM).data('vmid'));
-
- if(vmData.sessionState != 'Unlocked')
- thisSessionLocked = true;
-
-
-
- // Where was this dropped?
- var dropTarget = $('#'+vboxChooser._anchorid).find('td.vboxChooserDropTargetHover');
-
- // Dropped above / below a VM
- if(dropTarget[0]) {
-
- // Dropped from another group into this one,
- // but this group already has this VM
- if((dropTarget.closest('table').closest('div.vboxChooserGroup').data('vmGroupPath') != $(droppedVM).closest('div.vboxChooserGroup').data('vmGroupPath'))
- && dropTarget.closest('table').siblings('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+$(droppedVM).data('vmid'))[0]) {
- return true;
- }
-
- // If session of this VM is locked, don't allow it to be
- // dragged out of current group
- if(thisSessionLocked && !$('#vboxPane').data('vboxConfig')['phpVboxGroups'] && ($(droppedVM).closest('div.vboxChooserGroup').data('vmGroupPath') != dropTarget.closest('div.vboxChooserGroup').data('vmGroupPath'))) {
- return
- }
-
- // Get VM from target's parent table
- if(dropTarget.hasClass('vboxDropTargetTop')) {
- if(!e.ctrlKey && !e.metaKey) {
- $(droppedVM).detach().insertBefore($(dropTarget).closest('table'));
- } else {
- // Copy
- if($(dropTarget).closest('table').parent().children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmData.id)[0])
- return;
- vboxChooser.vmHTML(vmData).insertBefore($(dropTarget).closest('table'));
- }
- } else {
- if(!e.ctrlKey && !e.metaKey) {
- $(droppedVM).detach().insertAfter($(dropTarget).closest('table'));
- } else {
- // Copy - Don't allow if it already exists
- if($(dropTarget).closest('table').parent().children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmData.id)[0])
- return;
- vboxChooser.vmHTML(vmData).insertAfter($(dropTarget).closest('table'));
- }
- }
-
- // Not dropped above / below vm
- } else {
-
- // Don't allow this if sessoin is locked
- if(thisSessionLocked && !$('#vboxPane').data('vboxConfig')['phpVboxGroups']) return;
-
- // Dropped ON a vm?
- dropTarget = $('#'+vboxChooser._anchorid).find('table.vboxHover:not(.ui-draggable-dragging)').first();
- if($(dropTarget).data('vmid')) {
-
- // Create a group?
- dropTarget = $('#'+vboxChooser._anchorid).find('table.vboxHover').first();
-
- // Nothing to do. Not dropped on valid target
- if(!dropTarget[0] || ($(dropTarget).data('vmid') == $(droppedVM).data('vmid'))) return true;
-
- // Dont' allow this if target VM's session is locked
- if($(dropTarget).find('td.vboxVMSessionOpen')[0])
- return;
-
- // Where to drop vboxChooser..
- var p = dropTarget.closest('div.vboxChooserGroup').children('div.vboxChooserGroupVMs');
- // assume root?
- if(!p[0]) p = vboxChooser._anchor.children('div.vboxChooserGroupVMs');
-
- // Determine group name
- var gname = trans('New group','UIGChooserModel');
- var tgname = gname;
-
- var i = 2;
- while(vboxChooser.groupNameConflicts($(p).parent(), tgname)) {
- tgname = gname + ' ' + (i++);
- }
-
-
- // New position is below target
- var ghtml = vboxChooser.groupHTML(String(dropTarget.closest('div.vboxChooserGroup').data('vmGroupPath')+'/'+tgname).replace('//','/'));
-
- if(!e.ctrlKey && !e.metaKey) {
- ghtml.children('div.vboxChooserGroupVMs').append($(droppedVM).detach());
- } else {
- ghtml.children('div.vboxChooserGroupVMs').append(vboxChooser.vmHTML(vmData));
- }
- ghtml.children('div.vboxChooserGroupVMs').append(dropTarget.detach());
-
- ghtml.insertBefore(p);
-
- // Dropped in the main VM list or group header?
- } else {
-
- dropTarget = $(vboxChooser._anchor).find('div.vboxHover').first();
- if(dropTarget[0] && dropTarget.hasClass('vboxChooserGroupHeader')) {
-
- // Group already has this dragging VM?
- if(dropTarget.siblings('div.vboxChooserGroupVMs').children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+$(droppedVM).data('vmid'))[0]) {
- return;
- }
-
- if(!e.ctrlKey && !e.metaKey)
- $(droppedVM).detach().appendTo(dropTarget.siblings('div.vboxChooserGroupVMs').first());
- else
- vboxChooser.vmHTML(vmData).appendTo(dropTarget.siblings('div.vboxChooserGroupVMs').first());
-
- // Main VM list
- } else if($(vboxChooser._anchor).find('div.vboxGroupHover').length == 0 && $(vboxChooser._anchor).hasClass('vboxChooserDropTargetHoverRoot')) {
-
- dropTarget = null;
-
- // Only showing one group?
- if(vboxChooser._showOnlyGroupHistory.length > 0) {
- dropTarget = $(vboxChooser._showOnlyGroupHistory[vboxChooser._showOnlyGroupHistory.length-1]);
- }
-
- if(!$(dropTarget)[0])
- dropTarget = vboxChooser._anchor.children('div.vboxChooserGroup');
-
- // Already in this list?
- if(dropTarget.children('div.vboxChooserGroupVMs').children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+$(droppedVM).data('vmid'))[0]) {
- return true;
- }
-
- if(!e.ctrlKey && !e.metaKey) {
- $(droppedVM).detach().appendTo(dropTarget.children('div.vboxChooserGroupVMs').first());
- } else {
- vboxChooser.vmHTML(vmData).appendTo(dropTarget.children('div.vboxChooserGroupVMs').first());
- }
-
- }
- }
-
- }
-
- // vm dropped - compose and save group definitions
- vboxChooser.composeGroupDef(true);
-
- // Resize chooser elements
- vboxChooser._resizeElements();
-
- vboxChooser.selectionListChanged(vboxChooser._selectedList);
-
- },
-
- /*
- * Group selected items into a new group
- */
- groupSelectedItems : function() {
-
- // Get all group paths to determine new group target
- var groupPaths = {};
- vboxChooser._anchor.find('div.vboxVMGroupSelected').closest('div.vboxChooserGroup').each(function(idx,elm) {
- groupPaths[$(elm).data('vmGroupPath')] = 1;
- });
- vboxChooser._anchor.find('table.vboxListItemSelected').closest('div.vboxChooserGroup').each(function(idx,elm) {
- groupPaths[$(elm).data('vmGroupPath')] = 1;
- });
-
- // The group clsest to the root group will be the target
- var groupPathTarget = null;
- for(var i in groupPaths) {
-
- if(typeof(i) != 'string') continue;
-
- // Already at root group. Nothing to do
- if(groupPathTarget == '/') break;
-
- // No target set yet or equal targets, or this group is the root
- if(!groupPathTarget || groupPathTarget == i || i == '/') {
- groupPathTarget = i;
- continue;
- }
-
- var t1 = groupPathTarget.split("/");
- var t2 = i.split("/");
- for(var i = 0; i < Math.min(t1.length,t2.length); i++) {
- if(t1[i] != t2[i]) {
- groupPathTarget = '';
- for(var a = 0; a < i; a++) {
- groupPathTarget += "/" + t1[a];
- }
- groupPathTarget = groupPathTarget.replace('//','/');
- break;
- }
- }
-
-
- }
-
- var target = vboxChooser.getGroupElement(groupPathTarget, true);
-
- if(!$(target)[0]) return;
-
- // Determine group name
- var gname = trans('New group','UIGChooserModel');
- var tgname = gname;
-
- var i = 2;
- while(vboxChooser.groupNameConflicts($(target), tgname)) {
- tgname = gname + ' ' + (i++);
- }
-
- var gHTML = vboxChooser.groupHTML('/'+tgname);
-
- // Append group and vm elements
- vboxChooser._anchor.find('div.vboxVMGroupSelected').detach().insertAfter(gHTML.children('div.vboxChooserGroupHeader'));
- vboxChooser._anchor.find('table.vboxListItemSelected').detach().appendTo(gHTML.children('div.vboxChooserGroupVMs'));
-
- gHTML.insertBefore($(target).children('div.vboxChooserGroupVMs'));
-
- // group selected items,
- // Compose and save group definitions
- vboxChooser.composeGroupDef(true);
-
- // Resize chooser elements
- vboxChooser._resizeElements();
-
-
-
- },
-
- /**
- * Compose group data from GUI and optionally save it
- *
- * @param save - save group definitions to vbox
- */
- composeGroupDef : function(save) {
-
- var allGroups = [];
- var groupsResolved = false;
-
- // Keep looping through group definitions until
- // there are no groups removed
- while(!groupsResolved) {
-
- allGroups = [];
- groupsResolved = true;
-
- vboxChooser._anchor.find('div.vboxChooserGroup:not(.ui-draggable-dragging)').each(function(idx,elm) {
-
- // Group element was removed
- if(!$(elm)[0]) return;
-
- // Compose group path
- var myPath = $(elm).children('div.vboxChooserGroupIdentifier').attr('title');
- if(!myPath) myPath = '/';
- $(elm).parents('div.vboxChooserGroup:not(.ui-draggable-dragging)').each(function(idx2,elm2){
- var pName = $(elm2).children('div.vboxChooserGroupIdentifier').attr('title');
- if(!pName) pName = '/';
- myPath = String(pName + '/' + myPath).replace('//','/');
- });
-
- // Groups
- var gList = [];
- $(elm).children('div.vboxChooserGroup:not(.ui-draggable-dragging)').each(function(idx2,elm2){
-
- // If this group is selected, we'll have to update its path
- // in the selection list
- var selected = $(elm2).hasClass('vboxVMGroupSelected');
- var oldPath = $(elm2).data('vmGroupPath');
- var newPath = String(myPath + '/' + $(elm2).children('div.vboxChooserGroupIdentifier').attr('title')).replace('//','/');
-
- gList[gList.length] = $(elm2).children('div.vboxChooserGroupIdentifier').attr('title');
-
- // set / correct group path data
- $(elm2).data('vmGroupPath', newPath);
-
- // Group's path changed?
- if(selected && (oldPath != newPath)) {
- for(var i = 0; i < vboxChooser._selectedList.length; i++) {
- if(vboxChooser._selectedList[i].type == 'group' && vboxChooser._selectedList[i].groupPath == oldPath) {
- vboxChooser._selectedList[i].groupPath = String(myPath + '/' + $(elm2).children('div.vboxChooserGroupIdentifier').attr('title')).replace('//','/');
- break;
- }
- }
- }
-
- });
-
- // VMs
- var vmList = [];
- $(elm).children('div.vboxChooserGroupVMs').children('table.vboxChooserVM:not(.ui-draggable-dragging)').each(function(idx3,elm3){
- vmList[vmList.length] = $(elm3).data('vmid');
- });
-
- // Skip and remove if there are no VMs or subgroups
- // And it is not the parent group
- if(gList.length + vmList.length == 0 && !$(elm).hasClass('vboxChooserGroupRoot')) {
-
- // remove from selected list?
- if(elm && $(elm).hasClass('vboxVMGroupSelected')) {
-
- var myPath = $(elm).data('vmGroupPath');
- // Deselect item
- vboxChooser._selectedList = vboxChooser._selectedList.filter(function(v){
- return (v.type != 'group' || (v.groupPath != myPath));
- });
- }
- $(elm).empty().remove();
- groupsResolved = false;
- return false;
- }
-
- // append to all groups list
- gorder = [];
- if(gList.length) gorder[0] = 'go='+gList.join(',go=');
- if(vmList.length) gorder[gorder.length] = 'm='+vmList.join(',m=');
- allGroups[allGroups.length] = {
- path: $(elm).data('vmGroupPath'),
- order: gorder.join(',')
- };
-
- // Update counts span
- $(elm).children('div.vboxChooserGroupVMs').css({'display':(vmList.length || $(elm).data('vmGroupPath') == '/' ? '' : 'none')})
- .siblings('div.vboxChooserGroupHeader')
- .each(function(hidx,header) {
-
- var staticTip = '<strong>'+$(header).siblings('div.vboxChooserGroupIdentifier').attr('title')+'</strong>'+
- (gList.length ? ('<br />' + trans('%n group(s)','UIGChooserItemGroup',gList.length).replace('%n',gList.length)) : '') +
- (vmList.length ? ('<br />' + trans('%n machine(s)','UIGChooserItemGroup',vmList.length).replace('%n',vmList.length)) : '');
-
- $(header).tipped({'source':function() {
-
- // find number of running VMs
- var runningVMs = 0;
-
- if(vmList.length) {
- $(header).siblings('div.vboxChooserGroupVMs').find('td.vboxVMSessionOpen').each(function(idx,elm3) {
- if(vboxVMStates.isRunning(vboxVMDataMediator.getVMData($(elm3).closest('table').data('vmid'))))
- runningVMs++;
- });
- }
-
- return staticTip + (runningVMs > 0 ? ' ' + trans('(%n running)','UIGChooserItemGroup',runningVMs).replace('%n', runningVMs) : '');
- }
- ,'position':'mouse','delay':1500});
- })
- .children('span.vboxChooserGroupInfo')
- .children('span.vboxChooserGroupCounts').html(
- (gList.length ? ('<span style="background-image:url(images/vbox/group_abstract_16px.png);" />'+gList.length) : '') +
- (vmList.length ? ('<span style="background-image:url(images/vbox/machine_abstract_16px.png);" />'+vmList.length) : '')
- );
- });
-
- }
-
- // Save GUI group definition?
- if(!save) return;
-
-
- // Tell the interface we're about to save groups
- vboxChooser._editable = false;
- $('#vboxPane').trigger('vmGroupDefsSaving');
-
- vboxChooser._groupDefs = allGroups;
-
- // Save machine groups and trigger change
- var vms = [];
- var vmList = vboxVMDataMediator.getVMList();
- for(var i = 0; i < vmList.length; i++) {
-
- if(!vmList[i] || vmList[i].id == 'host') continue;
-
- /* If a VM's groups have changed, add it to the list */
- var eGroups = vmList[i].groups;
- var nGroups = vboxChooser.getGroupsForVM(vmList[i].id);
-
- if($(nGroups).not(eGroups).length || $(eGroups).not(nGroups).length) {
-
- vms[vms.length] = {
- 'id' : vmList[i].id,
- 'groups' : nGroups
- };
- }
- }
-
- // Save machines groups?
- if(vms.length) {
-
- // Reload VMs and group definitions
- var reloadAll = function() {
-
- var ml = new vboxLoader();
- ml.add('vboxGetMedia',function(d){$('#vboxPane').data('vboxMedia',d.responseData);});
- ml.add('vboxGroupDefinitionsGet',function(d){vboxChooser._groupDefs = d.responseData;});
-
- // Reload VM list and group definitions, something went wrong
- ml.onLoad = function() {
-
- // Stop vmlist from refreshing..
- vboxChooser.stop();
-
- // reset selections
- $('#vboxPane').trigger('vmSelectionListChanged',[vboxChooser]);
-
- // ask for new one
- vboxChooser.start();
- };
- ml.run();
- };
-
- $.when(vboxAjaxRequest('machinesSaveGroups',{'vms':vms})).done(function(res){
-
- if(res.responseData.errored) {
- reloadAll();
- vboxChooser._editable = true;
- $('#vboxPane').trigger('vmGroupDefsSaved');
- } else {
- $.when(vboxAjaxRequest('vboxGroupDefinitionsSet',{'groupDefinitions':allGroups})).always(function(){
- vboxChooser._editable = true;
- $('#vboxPane').trigger('vmGroupDefsSaved');
- });
- }
-
- }).fail(reloadAll);
-
- } else {
- $.when(vboxAjaxRequest('vboxGroupDefinitionsSet',{'groupDefinitions':allGroups})).always(function(){
- vboxChooser._editable = true;
- $('#vboxPane').trigger('vmGroupDefsSaved');
- });
- }
-
-
- return allGroups;
-
- },
-
- /*
- * Return a list of groups that VM is a member of
- */
- getGroupsForVM : function(vmid) {
- var gPathList = [];
- vboxChooser._anchor.find('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmid+':not(.ui-draggable-dragging)').each(function(idx,elm){
- var gParent = $(elm).closest('div.vboxChooserGroup');
- if(!gParent.hasClass('ui-draggable-dragging'))
- gPathList[gPathList.length] = gParent.data('vmGroupPath');
- });
- return gPathList;
- },
-
- /*
- * Determine whether or not a group name conflicts
- * with another group in parent
- */
- groupNameConflicts : function(parentGroup, name, ignoreGroupAtPath) {
- var found = false;
- parentGroup.children('div.vboxChooserGroup:not(.ui-draggable-dragging)').children('div.vboxChooserGroupIdentifier[title="'+name+'"]').parent().each(function(i,elm){
-
- if(ignoreGroupAtPath && (ignoreGroupAtPath == $(elm).data('vmGroupPath')))
- return true;
-
- found=true;
- return false;
- });
- return found;
- },
-
- /*
- * Ungroup selected group
- */
- unGroupSelectedGroup : function() {
-
- var target = $(vboxChooser.getSelectedGroupElements()[0]).siblings('div.vboxChooserGroupVMs');
-
- // Groups
- // - ignore group at path we are currently ungrouping
- var ignoreGroup = $(vboxChooser.getSelectedGroupElements()[0]).data('vmGroupPath');
- $(vboxChooser.getSelectedGroupElements()[0]).children('div.vboxChooserGroup').each(function(i,elm) {
-
- // Make sure there are no conflicts
- var newElm = $(elm).detach();
- var groupName = $(elm).children('div.vboxChooserGroupIdentifier').attr('title');
- var newGroupName = groupName;
-
- var i = 2;
- while(vboxChooser.groupNameConflicts($(target).parent(), newGroupName, ignoreGroup)) {
- newGroupName = groupName + ' (' + (i++) + ')';
- }
-
- $(newElm).children('div.vboxChooserGroupIdentifier').attr({'title':newGroupName})
- .siblings('div.vboxChooserGroupHeader')
- .children('span.vboxChooserGroupName').text(newGroupName);
-
- $(newElm).insertBefore(target);
-
- });
-
- // VMs
- $(vboxChooser.getSelectedGroupElements()[0]).children('div.vboxChooserGroupVMs').children().each(function(i,elm){
- $(elm).detach();
- if(!target.children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+$(elm).data('vmid'))[0])
- target.append(elm);
- });
-
-
- // ungroup selected items
- // compose and save group definitions
- vboxChooser.composeGroupDef(true);
-
- // Resize chooser elements
- vboxChooser._resizeElements();
-
- vboxChooser.selectionListChanged();
-
-
- },
-
- /*
- * Sort group sub-elements based on VirtualBox
- * group definitions
- */
- sortGroup : function(gElm) {
-
- // Find group orders
- var gPath = $(gElm).data('vmGroupPath');
- var groupList = vboxChooser._groupDefs;
-
- if(!(gPath && groupList)) return;
-
- var machineOrder = [];
- var groupOrder = [];
-
- // Get correct order
- for(var i = 0; i < groupList.length; i++) {
- if(groupList[i].path == gPath) {
- order = groupList[i].order.split(',');
- for(var a = 0; a < order.length; a++) {
- kv = order[a].split('=',2);
- if(kv[0] == 'm') machineOrder[machineOrder.length] = kv[1];
- else groupOrder[groupOrder.length] = kv[1];
- }
- }
- }
-
- // sort groups
- var groups = $(gElm).children('div.vboxChooserGroup').get();
- var maxPos = groups.length;
- groups.sort(function(a,b){
-
- var Pos1 = jQuery.inArray($(a).children('div.vboxChooserGroupIdentifier').attr('title'), groupOrder);
- var Pos2 = jQuery.inArray($(b).children('div.vboxChooserGroupIdentifier').attr('title'), groupOrder);
-
- if(Pos1==-1) Pos1 = maxPos;
- if(Pos2==-1) Pos2 = maxPos;
-
- return (Pos1 > Pos2 || Pos1 == -1 ? -1 : (Pos2 == Pos1 ? 0 : 1));
-
- });
- $.each(groups, function(idx,itm) {
- $(itm).insertAfter($(gElm).children('div.vboxChooserGroupHeader'));
- });
-
- // sort VMs
- var vms = $(gElm).children('div.vboxChooserGroupVMs').children('table.vboxChooserVM').get();
- var maxPos = vms.length;
- vms.sort(function(a,b) {
-
- var Pos1 = jQuery.inArray($(a).data('vmid'), machineOrder);
- var Pos2 = jQuery.inArray($(b).data('vmid'), machineOrder);
-
- if(Pos1==-1) Pos1 = maxPos;
- if(Pos2==-1) Pos2 = maxPos;
-
- return (Pos1 > Pos2 ? 1 : (Pos2 == Pos1 ? 0 : -1));
-
- });
- $.each(vms, function(idx,itm) {
- $(gElm).children('div.vboxChooserGroupVMs').append(itm);
- });
-
-
- },
-
- /*
- * Sort selected group by item names
- */
- sortSelectedGroup : function(rootElm) {
-
- var el = $(vboxChooser.getSelectedGroupElements()[0]);
-
- if(rootElm || !el) {
- el = vboxChooser._anchor.children('div.vboxChooserGroup');
- }
-
- // sort groups
- var groups = $(el).children('div.vboxChooserGroup').get();
- groups.sort(function(a,b){
- return $(b).children('div.vboxChooserGroupIdentifier').attr('title').localeCompare($(a).children('div.vboxChooserGroupIdentifier').attr('title'));
- });
- $.each(groups, function(idx,itm) {
- $(itm).insertAfter($(el).children('div.vboxChooserGroupHeader'));
- });
-
- // sort VMs
- var vms = $(el).children('div.vboxChooserGroupVMs').children('table.vboxChooserVM').get();
- vms.sort(function(a,b) {
- return $(a).find('span.vboxVMName').text().localeCompare($(b).find('span.vboxVMName').text());
- });
- $.each(vms, function(idx,itm) {
- $(el).children('div.vboxChooserGroupVMs').append(itm);
- });
-
- // compose and save group definitions
- vboxChooser.composeGroupDef(true);
-
- },
-
- /*
- * Rename selected group
- */
- renameSelectedGroup : function() {
-
- var el = $(vboxChooser.getSelectedGroupElements()[0]);
-
- // Function to rename group
- var renameGroup = function(e, textbox) {
-
- if(!textbox) textbox = $(this);
-
- var newName = $(textbox).val().replace(/[\\\/:*?"<>,]/g,'_');
-
- if(newName && newName != $(textbox).closest('div.vboxChooserGroup').children('div.vboxChooserGroupIdentifier').attr('title')) {
-
- // Do not rename if it conflicts
- var noConflict = newName;
- var i = 2;
- while(vboxChooser.groupNameConflicts($(textbox).parent().parent().parent().parent(), noConflict)) {
- noConflict = newName + ' (' + (i++) + ')';
- }
- newName = noConflict;
-
- $(textbox).closest('div.vboxChooserGroup')
- .children('div.vboxChooserGroupIdentifier').attr({'title':newName})
- .siblings('div.vboxChooserGroupHeader')
- .children('span.vboxChooserGroupName').html(newName);
-
- // group renamed, compose and save groups
- vboxChooser.composeGroupDef(true);
-
- // Write out collapsed group list
- vboxChooser._saveCollapsedGroups();
-
- }
-
-
- $(textbox).parent().parent().children().css({'display':''});
- $(textbox).parent().empty().remove();
- };
-
- $(el).children('div.vboxChooserGroupHeader').children().css({'display':'none'});
- $(el).children('div.vboxChooserGroupHeader').append(
-
- $('<form />').append(
- $('<input />').attr({'type':'text','value':$(el).children('div.vboxChooserGroupIdentifier').attr('title')}).css({'width':'90%','padding':'0px','margin':'0px'}).on('keypress',function(e){
- if (e.which == 13) {
- $(this).off('blur', renameGroup);
- renameGroup(e,this);
- e.stopPropagation();
- e.preventDefault();
- $(this).trigger('blur');
- return false;
- }
- })
- )
-
- );
- $(el).children('div.vboxChooserGroupHeader').children('form').children('input').focus().select().blur(renameGroup);
-
- },
-
- /*
- * Select a single group
- */
- _selectGroup : function(gelm) {
- $(gelm).addClass('vboxVMGroupSelected');
- },
-
- /*
- * Deselect a single group
- */
- _deselectGroup : function(gelm) {
-
- $(gelm).removeClass('vboxVMGroupSelected');
- },
-
- /*
- * Select (or unselect) an item in our list. Called onmousedown or onCLick
- */
- selectItem : function(e) {
-
- // Right click selects item if it is not selected
- if(e.which != 1) {
-
- // Right click on group header and group is selected
- // just return and show context menu
- if($(this).hasClass('vboxChooserGroupHeader') && $(this).parent().hasClass('vboxVMGroupSelected')) {
- return true;
-
- // Right click on VM and VM is already selected
- // just return and show context menu
- } else if($(this).hasClass('vboxListItemSelected')) {
- return true;
- }
- }
-
- var selectedList = [];
- var item = $(this);
-
-
- // Group?
- if($(item).hasClass('vboxChooserGroupHeader')) {
-
-
- // No control key. Exclusive selection
- if(!e.ctrlKey && !e.metaKey) {
-
- // already selected
- if(vboxChooser._selectedList.length == 1 && vboxChooser._selectedList[0].type == 'group' &&
- vboxChooser._selectedList[0].groupPath == $(item).parent().data('vmGroupPath'))
- return true;
-
- vboxChooser._anchor.find('.vboxListItemSelected').removeClass('vboxListItemSelected');
- vboxChooser._anchor.find('div.vboxVMGroupSelected')
- .each(function(idx,gelm) {
- vboxChooser._deselectGroup(gelm);
- });
-
-
-
- // select current group
- vboxChooser._selectGroup($(item).parent());
-
- selectedList = [{
- type: 'group',
- groupPath: $(item).parent().data('vmGroupPath')
- }];
-
- // Already selected, and ctrl key
- } else if($(item).parent().hasClass('vboxVMGroupSelected')){
-
- // Deselect item
- selectedList = vboxChooser._selectedList.filter(function(v){
- return (v.type != 'group' || (v.groupPath != $(item).parent().data('vmGroupPath')));
- });
-
- vboxChooser._deselectGroup($(item).parent());
-
- // Not already selected, and ctrl key
- } else {
-
- vboxChooser._selectGroup($(item).parent());
-
- selectedList = vboxChooser._selectedList;
-
- selectedList[selectedList.length] = {
- type: 'group',
- groupPath: $(item).parent().data('vmGroupPath')
- };
-
- }
-
-
- // VM
- } else {
-
- // No ctrl key or selection is host. Exclusive selection
- if((!e.ctrlKey && !e.metaKey) || $(item).data('vmid') == 'host') {
-
- vboxChooser._anchor.find('.vboxListItemSelected').removeClass('vboxListItemSelected');
- vboxChooser._anchor.find('div.vboxVMGroupSelected').removeClass('vboxVMGroupSelected')
- .each(function(idx,gelm){
- vboxChooser._deselectGroup(gelm);
- });
-
- // Select current VM
- $(item).addClass('vboxListItemSelected').removeClass('vboxHover');
-
- // already selected
- if(vboxChooser._selectedList.length == 1 && vboxChooser._selectedList[0].type == 'vm' &&
- vboxChooser._selectedList[0].id == $(item).data('vmid'))
- return true;
-
- selectedList = [{
- type: 'vm',
- id: $(item).data('vmid'),
- groupPath: $(item).parent().data('vmGroupPath')
- }];
-
- // Already selected, and ctrl key
- } else if($(item).hasClass('vboxListItemSelected')) {
-
- // Deselect item
- selectedList = vboxChooser._selectedList.filter(function(v){
- return (v.type == 'group' || (v.id != $(item).data('vmid')));
- });
-
- $(item).removeClass('vboxListItemSelected');
-
- // ctrl key, but not already selected
- } else {
-
- $(item).addClass('vboxListItemSelected').removeClass('vboxHover');
-
- selectedList = vboxChooser._selectedList;
-
- selectedList[selectedList.length] = {
- type: 'vm',
- id: $(item).data('vmid'),
- groupPath: $(item).parent().data('vmGroupPath')
- };
-
- }
-
- }
-
- // Remove host?
- if(selectedList.length > 1) {
-
- // Deselect host
- selectedList = selectedList.filter(function(v){
- return (v.type == 'group' || (v.id != 'host'));
- });
-
- vboxChooser._anchor.children('table.vboxChooserItem-'+vboxChooser._anchorid+'-host').removeClass('vboxListItemSelected');
-
- }
-
- vboxChooser.selectionListChanged(selectedList);
-
- return true;
-
-
- },
-
- /*
- * Show only single group element identified by gelm
- */
- showOnlyGroupElm : function(gelm) {
-
- // Going backwards affects animations
- var back = false;
-
- // gelm is null if we're going backwards
- if(!gelm) {
-
-
- if(vboxChooser._showOnlyGroupHistory.length > 1) {
- // this gets rid of current
- vboxChooser._showOnlyGroupHistory.pop();
- // selects previous
- gelm = vboxChooser._showOnlyGroupHistory.pop();
- back = true;
- } else {
- gelm = null;
- }
-
-
- } else {
-
- // Hold history
- vboxChooser._showOnlyGroupHistory[vboxChooser._showOnlyGroupHistory.length] = gelm;
- }
-
-
- // No scrolling
- vboxChooser._anchor.css({'overflow-y':'hidden'});
-
- if($(gelm)[0]) {
-
-
- // Slide over or back
- $.when(vboxChooser._anchor.hide('slide', {direction: (back ? 'right' : 'left'), distance: (vboxChooser._anchor.outerWidth()/1.5)}, 200)).always(function() {
-
-
- /* hide host when showing only a group */
- $('table.vboxChooserItem-'+vboxChooser._anchorid+'-host').hide();
-
-
- /* Undo anything previously performed by this */
- vboxChooser._anchor.find('div.vboxChooserGroupHide').removeClass('vboxChooserGroupHide vboxChooserGroupHideShowContainer');
- vboxChooser._anchor.find('div.vboxChooserGroupShowOnly').removeClass('vboxChooserGroupShowOnly');
-
- $(gelm).parents('div.vboxChooserGroup').addClass('vboxChooserGroupHide vboxChooserGroupHideShowContainer').siblings().addClass('vboxChooserGroupHide');
-
- vboxChooser._anchor.find('div.vboxChooserGroupRootLevel').removeClass('vboxChooserGroupRootLevel');
- $(gelm).addClass('vboxChooserGroupShowOnly vboxChooserGroupRootLevel').siblings().addClass('vboxChooserGroupHide');
-
- $.when(vboxChooser._anchor.show('slide', {direction: (back ? 'left' : 'right'), distance: (vboxChooser._anchor.outerWidth()/1.5)}, 200))
- .done(function(){
-
- // Restore scrolling
- vboxChooser._anchor.css({'overflow-y':'auto'});
-
- // Hide group info
- $(gelm).find('div.vboxChooserGroupHeader').trigger('mouseout');
-
- // Reset title sizes
- vboxChooser._resizeElements();
-
- // force redraw of these
- $(gelm).find('.vboxFitToContainer').css({'display':'none'}).css({'width':'','display':''});
-
-
- });
-
- });
-
- } else {
-
- vboxChooser._showOnlyGroupHistory = [];
-
- // Slide back to anchor
- $.when(vboxChooser._anchor.hide('slide', {direction: 'right', distance: (vboxChooser._anchor.outerWidth()/1.5)}, 200)).always(function() {
-
- /* show host when going back to main list */
- $('table.vboxChooserItem-'+vboxChooser._anchorid+'-host').show();
-
- vboxChooser._anchor.find('div.vboxChooserGroupHide').removeClass('vboxChooserGroupHide vboxChooserGroupHideShowContainer');
- vboxChooser._anchor.find('div.vboxChooserGroupShowOnly').removeClass('vboxChooserGroupShowOnly ');
-
- vboxChooser._anchor.find('div.vboxChooserGroupRootLevel').removeClass('vboxChooserGroupRootLevel');
- vboxChooser._anchor.children('div.vboxChooserGroupRoot').addClass('vboxChooserGroupRootLevel');
-
- $.when(vboxChooser._anchor.show('slide', {direction: 'left', distance: (vboxChooser._anchor.outerWidth()/1.5)}, 200))
- .done(function(){
-
- // Restore scrolling
- vboxChooser._anchor.css({'overflow-y':'auto'});
-
- // Hide group info
- vboxChooser._anchor.find('div.vboxChooserGroupHeader').trigger('mouseout');
-
- // Reset title sizes
- vboxChooser._resizeElements();
-
- // force redraw of these
- vboxChooser._anchor.find('.vboxFitToContainer').css({'display':'none','width':''}).css({'display':''});
- });
- });
- }
-
- },
-
- /*
- * Return HTML for group
- */
- groupHTML : function(gpath) {
-
- if(!gpath) gpath = '/';
- var first = gpath == '/';
- var gname = gpath.substring(gpath.lastIndexOf('/')+1);
- var collapsed = vboxChooser._isGroupCollapsed(gpath);
-
- var gHTML = $('<div />').append(
- $('<div />').addClass('vboxChooserGroupIdentifier').css({'display':'none'}).attr({'title':gname})
- ).append(
- $('<div />').addClass('vboxChooserGroupHeader').css({'display':(first ? 'none' : '')})
- .attr({'title':gname})
- .dblclick(function() {
-
- // Already collapsed?
- var collapsed = $(this).closest('div.vboxChooserGroup').hasClass('vboxVMGroupCollapsed');
-
- // Button rotation function
- var rotateButton = function(){return true;};
-
- var vboxArrowImage = $(this).find('span.vboxChooserGroupNameArrowCollapse');
-
- if(!($.browser.msie && $.browser.version.substring(0,1) < 9)) {
-
- rotateButton = function() {
-
- return $('<div />').animate({left:90},{
- duration: 300,
- step: function(currentStep) {
- if(!collapsed) currentStep = (90 - currentStep);
- vboxArrowImage.css({
- 'transform':'rotate('+currentStep+'deg)',
- '-moz-transform': 'rotate('+currentStep+'deg)',
- '-webkit-transform': 'rotate('+currentStep+'deg)',
- '-o-transform': 'rotate('+currentStep+'deg)',
- '-ms-transform': 'rotate('+currentStep+'deg)'
- });
- },
- queue: true,
- complete: function() {
- vboxArrowImage.css({
- 'transform':'',
- '-moz-transform': '',
- '-webkit-transform': '',
- '-o-transform': '',
- '-ms-transform': ''
- });
- }
-
- });
- };
- }
-
-
- // Run button rotation and toggle class
- $.when(rotateButton(), $(this).closest('div.vboxChooserGroup').toggleClass('vboxVMGroupCollapsed', ($.browser.msie && $.browser.version.substring(0,1) < 9) ? undefined : 300)).always(function(){
-
- // Write out collapsed group list
- vboxChooser._saveCollapsedGroups();
-
- // Reset title sizes
- vboxChooser._resizeElements();
- });
-
-
- })
- .append(
- $('<div />').addClass('vboxChooserDropTarget')
- .addClass('vboxDropTargetTop').hover(function(){
- if(vboxChooser._draggingGroup)
- $(this).addClass('vboxChooserDropTargetHover' + (first ? 'ignore' : ''));
- }, function(){
- $(this).removeClass('vboxChooserDropTargetHover');
- })
- )
- .append(
- $('<span />').addClass('vboxChooserGroupNameArrowLeft vboxChooserGroupNameArrowCollapse vboxArrowImage')
- .mousedown(function(e){
- e.stopPropagation();
- e.preventDefault();
- return false;
- }).mouseup(function(){
- $(this).closest('div.vboxChooserGroupHeader').trigger('dblclick');
- })
-
- ).append(
-
- $('<span />').addClass('vboxChooserGroupNameArrowLeft vboxChooserGroupShowOnlyBack vboxArrowImage')
- .click(function(e) {
- e.stopPropagation();
- e.preventDefault();
- vboxChooser.showOnlyGroupElm();
- return false;
-
- })
-
- )
- .append($('<span />').addClass('vboxChooserGroupInfo').html(
- "<span class='vboxChooserGroupCounts' />"
- ).append(
- $('<span />').addClass('vboxChooserGroupShowOnly vboxArrowImage')
- .click(function(e){
- e.stopPropagation();
- e.preventDefault();
- vboxChooser.showOnlyGroupElm($(this).closest('div.vboxChooserGroup'));
- return false;
- })
-
- ))
- .append($('<span />').html(gname).addClass('vboxChooserGroupName vboxFitToContainer'))
- .append(
- $('<div />').addClass('vboxChooserDropTarget vboxChooserDropTargetBottom')
- .hover(function(){
- if(vboxChooser._draggingGroup)
- $(this).addClass('vboxChooserDropTargetHover' + (first ? 'ignore' : ''));
- }, function(){
- $(this).removeClass('vboxChooserDropTargetHover');
- })
-
- )
- .hover(function(){
-
- if(vboxChooser._compact) return;
-
- $(this).addClass('vboxHover');
-
- // Resize title and add hover class?
- if(!$(this).parent().hasClass('vboxChooserGroupRoot')) {
-
- // Set width of title to -group info span width
- var infoWidth = $(this).children('span.vboxChooserGroupInfo').width();
- var pWidth = $(this).width();
-
- $(this).children('span.vboxChooserGroupName').css({'max-width':(pWidth-infoWidth-20)+'px'});
-
- }
-
-
- },function(){
-
- // Resize title and remove hover class
- $(this).removeClass('vboxHover');
-
- if(!$(this).parent().hasClass('vboxChooserGroupRoot'))
- $(this).children('span.vboxChooserGroupName').css({'max-width':''});
-
- }).on('mousedown',vboxChooser.selectItem)
-
- ).addClass((first ? 'vboxChooserGroupRoot vboxChooserGroupRootLevel ' : (collapsed ? 'vboxVMGroupCollapsed ' : '')) + 'vboxChooserGroup')
- .data({'vmGroupPath':gpath})
- .draggable({'cursorAt':{left: -10, top: -10},'helper':function(){
-
- return $(this).clone().addClass('vboxVMGroupCollapsed vboxVMGroupSelected')
- .children('div.vboxChooserGroupHeader').removeClass('vboxHover').children('.vboxChooserGroupNameArrowCollapse')
- .hide().closest('div.vboxChooserGroup').css({'width':$(this).width()+'px'});
-
-
- },'start':function() {
-
- if(!$('#vboxPane').data('vboxSession').admin) return false;
-
- if(!vboxChooser._editable) return false;
-
- vboxChooser._draggingGroup = $(this).data('vmGroupPath');
- $(vboxChooser._anchor).disableSelection();
-
- },'stop':function(e) {
- vboxChooser.vmGroupDropped(e,$(this));
-
- }}).append($('<div />').addClass('vboxChooserGroupVMs'));
-
-
- // Bottom drop target
- if(!first) {
- gHTML.hover(function(){
- $(this).addClass('vboxGroupHover'); }, function() {
- $(this).removeClass('vboxGroupHover');
- }).append(
- $('<div />').addClass('vboxChooserDropTarget vboxChooserDropTargetBottom')
- .hover(function(){
- if(vboxChooser._draggingGroup)
- $(this).addClass('vboxChooserDropTargetHover');
- }, function(){
- $(this).removeClass('vboxChooserDropTargetHover');
- })
- );
- }
-
-
-
- // Group context menu
- $(gHTML).contextMenu({
- menu: vboxChooser._vmGroupContextMenuObj.menuId(),
- menusetup: function(el) {
- $(el).children('div.vboxChooserGroupHeader').trigger('click');
- }
- },function(act,el,pos,d,e){
- vboxChooser._vmGroupContextMenuObj.menuClickCallback(act, el);
- });
-
- return gHTML;
-
-
-
- },
-
- /*
- * Return selected VM elements
- */
- getSelectedVMElements : function() {
- return vboxChooser._anchor.find('table.vboxSelected');
- },
-
- /*
- * Return selected group elements
- */
- getSelectedGroupElements : function() {
- return vboxChooser._anchor.find('div.vboxVMGroupSelected');
- },
-
-
- /*
- * Start VM list update
- */
- start : function(anchorid) {
-
- // already running?
- if(vboxChooser._running) return;
- vboxChooser._running = true;
-
- // Where are we drawn?
- if(anchorid) {
- vboxChooser._anchorid = anchorid;
- vboxChooser._anchor = $('#'+anchorid);
- }
-
-
- // Set group definition key
- vboxChooser._groupDefinitionKey = $('#vboxPane').data('vboxConfig')['groupDefinitionKey'];
-
- // Get collapsed group list
- vboxChooser._collapsedGroups = vboxGetLocalDataItem($('#vboxPane').data('vboxConfig').key+'-collapsedGroups', true);
- if(!vboxChooser._collapsedGroups) vboxChooser._collapsedGroups = [];
- else vboxChooser._collapsedGroups = vboxChooser._collapsedGroups.split(',');
-
-
- // Get groups and machine list. datamediator will start listener
- $.when(vboxAjaxRequest('vboxGroupDefinitionsGet')).done(function(g) {
-
- vboxChooser._groupDefs = g.responseData;
-
- $.when(vboxVMDataMediator.getVMList()).done(function(d) {
- vboxChooser.updateList(d);
- });
- });
-
- },
-
- /*
- * Stop VM list updates and clear list
- */
- stop : function() {
-
- if(!vboxChooser._running) return;
- vboxChooser._running = false;
-
- vboxChooser._anchor.html("<div id='vboxChooserSpinner' style='text-align: center'><div><img src='images/spinner.gif' /></div></div>");
-
- // reset vars
- vboxChooser._versionChecked = false;
- vboxChooser._selectedList = [];
- vboxChooser.selectedVMs = [];
- vboxChooser.selectionMode = vboxSelectionModeNone;
-
- }
-
-
-
-};
-
-$(document).ready(function(){
-
- // Calculate scrollbar width
- vboxChooser._scrollbarWidth = getScrollbarWidth();
-
- // "Stop" chooser
- $('#vboxPane').on('hostChange',function(){
-
- vboxChooser.stop();
-
- }).on('hostChanged',function(){
-
-
- vboxChooser.start();
-
- // Refresh menus
- }).on('vmGroupDefsSaving vmGroupDefsSaved vmSelectionListChanged', function() {
-
- if(vboxChooser._vmGroupContextMenuObj)
- vboxChooser._vmGroupContextMenuObj.update(vboxChooser);
- if(vboxChooser._vmContextMenuObj)
- vboxChooser._vmContextMenuObj.update(vboxChooser);
-
-
- // Event list queue
- }).on('vboxEvents',function(e, eventList) {
-
- var redrawVMs = [];
- var sortGroups = [];
- var groupsChanged = false;
- var selectedChanged = false;
- var resizeElements = false;
-
- for(var i = 0; i < eventList.length; i++) {
-
- switch(eventList[i].eventType) {
-
- ////////////////////////////////
- //
- // Machine data changed
- //
- ////////////////////////////////
- case 'OnMachineDataChanged':
-
- // Shorthand
- var vmid = eventList[i].machineId;
- var data = vboxVMDataMediator.getVMData(vmid);
-
- // Update VM in list
- if(data) {
-
- // Enforce VM ownership
- if($('#vboxPane').data('vboxConfig').enforceVMOwnership && !$('#vboxPane').data('vboxSession').admin && data.owner != $('#vboxPane').data('vboxSession').user) {
- break;
- }
-
- redrawVMs[redrawVMs.length] = vmid;
-
- // Make sure VM has root group at least
- if(data.groups.length == 0) data.groups = ['/'];
-
- // Remove from groups if they have changed
- var currGroups = vboxChooser.getGroupsForVM(vmid);
- var groupDiff = $(currGroups).not(data.groups);
- groupsChanged = groupDiff.length;
- for(var a = 0; a < groupDiff.length; a++) {
-
- var gElm = vboxChooser.getGroupElement(groupDiff[a], false);
- if(!$(gElm)[0]) return;
-
- selectedChanged = (selectedChanged || $(gElm).children('div.vboxChooserGroupVMs').closest('div.vboxVMGroupSelected').length);
-
- $(gElm).children('div.vboxChooserGroupVMs')
- .children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+data.id).empty().remove();
-
- }
-
- // Add to other groups
- var groupDiff = $(data.groups).not(currGroups);
- groupsChanged = (groupsChanged || groupDiff.length);
- for(var a = 0; a < groupDiff.length; a++) {
-
- var gElm = vboxChooser.getGroupElement(groupDiff[a]);
-
- // Skip it if it is already there
- if($(gElm).children('div.vboxChooserGroupVMs').children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+data.id)[0])
- continue;
-
- $(gElm).children('div.vboxChooserGroupVMs')
- .append(
- vboxChooser.vmHTML(data)
- );
-
- selectedChanged = (selectedChanged || $(gElm).children('div.vboxChooserGroupVMs').closest('div.vboxVMGroupSelected').length);
-
- // Sort the group this machine was added to
- sortGroups = sortGroups.concat(data.groups);
-
- }
-
- resizeElements = (resizeElements || groupsChanged);
-
-
- }
-
- break;
-
- /////////////////////////////////
- //
- // Snapshot taken / deleted / restored
- //
- /////////////////////////////////
- case 'OnSnapshotDeleted':
- case 'OnSnapshotTaken':
- case 'OnSnapshotRestored':
- case 'OnSnapshotChanged':
- redrawVMs[redrawVMs.length] = eventList[i].machineId;
- break;
-
- /////////////////////////////////////
- //
- // Machine registered or unregistered
- //
- //////////////////////////////////////
- case 'OnMachineRegistered':
-
- // Shorthand
- var vmid = eventList[i].machineId;
-
- // Unregistered
- if(!eventList[i].registered) {
-
- var wasSelected = vboxChooser.isVMSelected(vmid);
-
- $('#'+vboxChooser._anchorid +' table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmid).remove();
-
- groupsChanged = true;
-
- // See if VM was selected
- if(wasSelected) {
-
- selectedChanged = true;
-
- vboxChooser._selectedList = vboxChooser._selectedList.filter(function(v){
- return (v.type == 'group' || (v.id != vmid));
- });
-
-
- }
-
- resizeElements = true;
-
-
- break;
-
- }
-
- // Registered
-
- // Enforce VM ownership
- if($('#vboxPane').data('vboxConfig').enforceVMOwnership && !$('#vboxPane').data('vboxSession').admin && eventList[i].enrichmentData.owner != $('#vboxPane').data('vboxSession').user) {
- break;
- }
-
- // Add to list
- vboxChooser.updateVMElement(eventList[i].enrichmentData, true);
-
- resizeElements = true;
- break;
-
-
- ///////////////////////////////////
- //
- // Extra data changed
- //
- ////////////////////////////////////
- case 'OnExtraDataChanged':
-
- if(!eventList[i].machineId && eventList[i].key.indexOf(vboxChooser._groupDefinitionKey) === 0) {
-
- var path = eventList[i].key.substring(vboxChooser._groupDefinitionKey.length);
- if(!path) path = "/";
- var name = path.substring(path.lastIndexOf('/')+1);
- var vboxVMGroups = vboxChooser._groupDefs;
- var found = false;
-
- // No current group definitions?
- if(!vboxVMGroups) break;
-
- // Step through each group, comparing
- for(var a = 0; a < vboxVMGroups.length; a++) {
- if(vboxVMGroups[a].path == path) {
- // Sort this group if it is different
- if(vboxVMGroups[a].order != eventList[i].value)
- sortGroups[sortGroups.length] = path;
- found = true;
- vboxVMGroups[a] = {'path':path,'name':name,'order':eventList[i].value};
- break;
- }
- }
-
- // Add to group if not found
- if(!found) {
- vboxVMGroups[vboxVMGroups.length] = {'path':path,'name':name,'order':eventList[i].value};
- sortGroups[sortGroups.length] = path; // sort when added
- resizeElements = true;
- }
-
- } else {
-
- switch(eventList[i].key) {
-
- // redraw when custom icon changes
- case 'phpvb/icon':
- redrawVMs[redrawVMs.length] = eventList[i].machineId;
- break;
- }
- }
- break;
-
- ////////////////////////////////////////
- //
- // Session or state change gets redrawn
- //
- ///////////////////////////////////////
- case 'OnSessionStateChanged':
- case 'OnMachineStateChanged':
- redrawVMs[redrawVMs.length] = eventList[i].machineId;
- break;
-
- } // </ switch eventType >>
-
-
- } // </ for each event >
-
- // Now redraw each VM
- ///////////////////////////
- var redrawn = {};
- var updateMenus = false;
- for(var i = 0; i < redrawVMs.length; i++) {
-
- if(redrawn[redrawVMs[i]]) continue;
- redrawn[redrawVMs[i]] = true;
-
- vboxChooser.updateVMElement(vboxVMDataMediator.getVMData(redrawVMs[i]));
-
- // Update menus if the VM is selected
- updateMenus = (updateMenus || vboxChooser.isVMSelected(redrawVMs[i]));
-
- }
-
- // Sort groups
- var groupsSorted = {};
- for(var i = 0; i < sortGroups.length; i++) {
- if(groupsSorted[sortGroups[i]]) continue;
- groupsSorted[sortGroups[i]] = true;
- var gElm = $(vboxChooser.getGroupElement(sortGroups[i]),false);
- if(gElm[0])
- vboxChooser.sortGroup(gElm);
-
- }
-
- // Groups changed
- if(groupsChanged || sortGroups.length) {
- vboxChooser.composeGroupDef();
- }
-
-
- // update selection list
- if(selectedChanged) {
-
- vboxChooser.selectionListChanged(vboxChooser._selectedList);
-
- } else if(updateMenus) {
-
- if(vboxChooser._vmGroupContextMenuObj)
- vboxChooser._vmGroupContextMenuObj.update(vboxChooser);
-
-
- if(vboxChooser._vmContextMenuObj)
- vboxChooser._vmContextMenuObj.update(vboxChooser);
-
- }
-
- if(resizeElements) vboxChooser._resizeElements(true);
-
-
-
- });
-
-
-
+/** + * + * @fileOverview Chooser (vm list) singleton. Provides vboxChooser + * @author Ian Moore (imoore76 at yahoo dot com) + * @version $Id: chooser.js 591 2015-04-11 22:40:47Z imoore76 $ + * @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com) + * + */ + +/** + * Chooser selection mode constants + */ +var vboxSelectionModeNone = 0; +var vboxSelectionModeSingleVM = 1; +var vboxSelectionModeMultiVM = 2; +var vboxSelectionModeSingleGroup = 3; + + +/** + * @namespace vboxChooser + * + * Draws machine selection chooser and controls selection list + * @see js/eventlistener.js + */ +var vboxChooser = { + + // VM list + vms : {}, + + // VM tool tip + _vmToolTip : '<nobr>%1<br></nobr><nobr>%2 since %3</nobr><br><nobr>Session %4</nobr>', + + // Anchor element + _anchorid : null, + _anchor : null, + + /* Internal list of all unique selected items */ + _selectedList : [], + + /* List of unique selected VMs */ + selectedVMs : [], + + /* Holds group definitions */ + _groupDefs : [], + + /* selection mode can be + + var vboxSelectionModeNone = 0, + var vboxSelectionModeSingleVM = 1, + var vboxSelectionModeMultiVM = 2, + var vboxSelectionModeSingleGroup = 3, + */ + selectionMode : vboxSelectionModeNone, + + /* Check phpVirtualBox version and VirtualBox + * version compatibility. + */ + _versionChecked : false, + + /* Some items are not editable while vmGroup + * definitions are being written + */ + _editable : true, + + /* Context menus */ + _vmContextMenuObj : null, + _vmGroupContextMenuObj : null, + + /* Holds history of showing only single groups */ + _showOnlyGroupHistory : [], + + /* Group definition extra value key */ + _groupDefinitionKey : '', + + /* Whether chooser is in compact mode or not */ + _compact : false, + + /** + * Set anchor id to draw to + */ + setAnchorId : function(aid) { + vboxChooser._anchorid = aid; + vboxChooser._anchor = $('#'+aid); + + vboxChooser._anchor.html("<div id='vboxChooserSpinner' style='text-align: center'><div><img src='images/spinner.gif' /></div></div>"); + + vboxChooser._anchor.hover(function(){ + $(this).addClass('vboxChooserDropTargetHoverRoot'); + },function() { + $(this).removeClass('vboxChooserDropTargetHoverRoot'); + }); + + $(window).resize(function(){ + + // Get anchor id and add / remove class + var w = parseInt($(vboxChooser._anchor).innerWidth()); + if(w < 120) { + $(vboxChooser._anchor).addClass('vboxChooserMini'); + vboxChooser._compact = true; + } else { + $(vboxChooser._anchor).removeClass('vboxChooserMini'); + vboxChooser._compact = false; + } + + vboxChooser._resizeElements(true); + + }); + }, + + /** + * Set context menus + * + */ + setContextMenu : function(target, menuitems) { + + switch(target) { + + // Group menu + case 'group': + vboxChooser._vmGroupContextMenuObj = new vboxMenu({'name': vboxChooser._anchorid+'vmgroups', + 'menuItems': menuitems, + 'language_context': 'UIActionPool'}); + vboxChooser._vmGroupContextMenuObj.update(); + break; + + // VM Menu + case 'vm': + vboxChooser._vmContextMenuObj = new vboxMenu({'name': vboxChooser._anchorid+'vms', + 'menuItems': menuitems, + 'language_context': 'UIActionPool'}); + vboxChooser._vmContextMenuObj.update(); + break; + + // Main list menu + case 'anchor': + + var vboxChooserPaneMenu = new vboxMenu({'name': vboxChooser._anchorid+'Pane', + 'menuItems': menuitems, + 'language_context': 'UIActionPool'}); + $('#'+vboxChooser._anchorid).parent().contextMenu({ + menu: vboxChooserPaneMenu.menuId() + }, + vboxChooserPaneMenu.menuClickCallback + ); + + break; + + default: + vboxAlert('vboxChooser::setContextMenu: unknown context menu type (' + target + ')'); + } + }, + + /* + * Return true if a selected VM is in the given state + */ + isSelectedInState : function(state) { + + for(var i = 0; i < vboxChooser.selectedVMs.length; i++) { + if(vboxVMStates['is'+state](vboxVMDataMediator.getVMData(vboxChooser.selectedVMs[i]))) + return true; + } + return false; + + }, + + /** + * Return true if the passed VM is selected + */ + isVMSelected : function(vmid) { + return (jQuery.inArray(vmid,vboxChooser.selectedVMs) > -1); + }, + + /** + * Return selected VM data in array + */ + getSelectedVMsData : function() { + + var vms = []; + for(var i = 0; i < vboxChooser.selectedVMs.length; i++) { + vms[vms.length] = vboxVMDataMediator.getVMData(vboxChooser.selectedVMs[i]); + } + return vms; + }, + + /** + * Triggered when selection list has changed + */ + selectionListChanged : function(selectionList) { + + if(!selectionList) selectionList = []; + + selectionMode = vboxSelectionModeNone; + + // Hold unique selected VMs + var vmListUnique = {}; + for(var i = 0; i < selectionList.length; i++) { + if(selectionList[i].type == 'group') { + vboxChooser.getGroupElement(selectionList[i].groupPath, true).find('table.vboxChooserVM:not(.ui-draggable-dragging)').each(function(idx,elm){ + if(elm) { + var vmid = $(elm).data('vmid'); + if(vmid) + vmListUnique[vmid] = vmid; + } + }); + switch(selectionMode) { + case vboxSelectionModeSingleGroup: + case vboxSelectionModeSingleVM: + selectionMode = vboxSelectionModeMultiVM; + break; + case vboxSelectionModeNone: + selectionMode = vboxSelectionModeSingleGroup; + } + } else { + switch(selectionMode) { + case vboxSelectionModeNone: + selectionMode = vboxSelectionModeSingleVM; + break; + default: + selectionMode = vboxSelectionModeMultiVM; + } + + vmListUnique[selectionList[i].id] = selectionList[i].id; + } + } + + // Change selection list + var selectedVMs = []; + for(var i in vmListUnique) { + selectedVMs[selectedVMs.length] = i; + } + + vboxChooser.selectedVMs = selectedVMs; + + // If there is only one unique vm selected, + // selection mode becomes single VM if the + // current selection mode is not singleGroup + if(vboxChooser.selectedVMs.length == 1 && selectionMode != vboxSelectionModeSingleGroup) + selectionMode = vboxSelectionModeSingleVM; + + vboxChooser.selectionMode = selectionMode; + + vboxChooser._selectedList = selectionList; + + $('#vboxPane').trigger('vmSelectionListChanged',[vboxChooser]); + + + }, + + /** + * Return the single selected VM's id if + * only one vm is selected. Else null. + */ + getSingleSelectedId : function() { + if(vboxChooser.selectedVMs.length == 1) { + return vboxChooser.selectedVMs[0]; + } + return null; + }, + + /* + * Return a single vm if only one is selected. + * Else null. + */ + getSingleSelected : function() { + if(vboxChooser.selectedVMs.length == 1) { + return vboxVMDataMediator.getVMData(vboxChooser.selectedVMs[0]); + } + return null; + }, + + /* + * Update list of VMs from data received + * from ajax query + */ + updateList : function(vmlist) { + + // We were stopped before the request returned data + if(!vboxChooser._running) return; + + + // No list? Something is wrong + if(!vmlist) { + + phpVirtualBoxFailure(); + + vboxChooser.stop(); + vboxChooser._anchor.children().remove(); + return; + } + + // Remove spinner + vboxChooser._anchor.children().remove(); + + // Render host + vboxChooser._anchor.append(vboxChooser.vmHTML( + { + 'id':'host', + 'state':'Hosting', + 'owner':'', + 'name':$('#vboxPane').data('vboxConfig').name, + 'OSTypeId':'VirtualBox_Host' + } + )); + + // Render root group + vboxChooser._anchor.append(vboxChooser.groupHTML("/")); + + // Enforce VM ownership + if($('#vboxPane').data('vboxConfig').enforceVMOwnership && !$('#vboxPane').data('vboxSession').admin) { + vmlist = jQuery.grep(vmlist,function(vm,i){ + return (vm.owner == $('#vboxPane').data('vboxSession').user); + }); + } + + var groups = []; + // Each item in list + for(var i = 0; i < vmlist.length; i++) { + // Update + vboxChooser.updateVMElement(vmlist[i], true); + groups = groups.concat(vmlist[i].groups); + } + + // Sort groups + var groupsSorted = {}; + for(var i = 0; i < groups.length; i++) { + if(groupsSorted[groups[i]]) continue; + groupsSorted[groups[i]] = true; + var gElm = vboxChooser.getGroupElement(groups[i], true); + if(gElm[0]) vboxChooser.sortGroup(gElm); + } + + // compose group definitions + vboxChooser.composeGroupDef(); + + // Set initial resize + vboxChooser._initialResize = true; + vboxChooser._resizeElements(true); + + }, + + /* + * Save collapsed group list + */ + _collapsedGroups : [], + _saveCollapsedGroups : function(){ + + // Write out collapsed group list + var cGroupList = []; + vboxChooser._anchor.find('div.vboxVMGroupCollapsed:not(.ui-draggable-dragging)').each(function(idx,elm) { + cGroupList[cGroupList.length] = $(elm).data('vmGroupPath'); + }); + + var groupListKey = $('#vboxPane').data('vboxConfig').key+'-collapsedGroups'; + vboxSetLocalDataItem(groupListKey, cGroupList.join(','), true); + + // Cache instead of using local storage + vboxChooser._collapsedGroups = cGroupList; + }, + + /* + * Return true if group is collapsed + */ + _isGroupCollapsed : function(gpath) { + return(jQuery.inArray(gpath,vboxChooser._collapsedGroups) > -1); + }, + + /* + * Resize group and VM titles + */ + _scrollbarWidth : 0, + _scrollbarWasVisible: false, + _initialResize: false, + _resizeElements : function(forceResize) { + + // Haven't completed our initial resizing yet + if(!vboxChooser._initialResize) { + return; + } + + var sbVisible = (vboxChooser._anchor.get(0).scrollHeight > vboxChooser._anchor.height()); + + // Nothing changed since resize + if(!forceResize && (sbVisible == vboxChooser._scrollbarWasVisible)) { + return; + } + + vboxChooser._scrollbarWasVisible = sbVisible; + + var groupTitleWidth = vboxChooser._anchor.width() - (vboxChooser._compact ? 22 : 32) - (sbVisible ? vboxChooser._scrollbarWidth : 0); + var vmTitleWidth = groupTitleWidth - (vboxChooser._compact ? -12 : 18); // (2px padding on .vboxChooserGroupVMs + + // 2px border on table + 4px margin on icon) * 2 + var groupLevelOffset = (vboxChooser._compact ? 8 : 8); // (2px margin + 2px border) * 2 + + + // Now that we have sizes, we can inject styles + $('#vboxChooserStyle').empty().remove(); + + var styleRules = []; + var path = ['div.vboxChooserGroupRootLevel']; + + // Special case for root level VM list + styleRules[styleRules.length] = 'div.vboxChooserGroupRootLevel > div.vboxChooserGroupVMs table.vboxChooserVM div.vboxFitToContainer { width: ' + vmTitleWidth + 'px; }'; + + // Special case for group header when only showing one group + styleRules[styleRules.length] = 'div.vboxChooserGroupShowOnly.vboxChooserGroupRootLevel > div.vboxChooserGroupHeader span.vboxChooserGroupName { max-width: ' + (groupTitleWidth - 4) + 'px; }'; + + // Bottom group resize bars + styleRules[styleRules.length] = 'div.vboxChooserGroupRootLevel > div.vboxChooserDropTargetBottom { width: ' + (groupTitleWidth) + 30 + 'px; }'; + + for(var i = 1; i < 11; i++) { + + // Group titles at this level + styleRules[styleRules.length] = path.join(' > ') + ' > div.vboxChooserGroup > div.vboxChooserGroupHeader span.vboxChooserGroupName { max-width: ' + (groupTitleWidth - (i*groupLevelOffset)) + 'px; }'; + + // VM titles at this level + styleRules[styleRules.length] = path.join(' > ') + ' > div.vboxChooserGroup > div.vboxChooserGroupVMs table.vboxChooserVM div.vboxFitToContainer { width: ' + (vmTitleWidth - (i*(groupLevelOffset))) + 'px; }'; + + // Bottom group resize bars + styleRules[styleRules.length] = path.join(' > ') +' > div.vboxChooserGroup > div.vboxChooserDropTargetBottom { width: ' + (groupTitleWidth + 30 - (i*groupLevelOffset)) + 'px; }'; + + path[path.length] = 'div.vboxChooserGroup'; + } + + // Style for minified vmlist + if(vboxChooser._compact) { + // Title moves left + styleRules[styleRules.length] = 'div.vboxChooserGroup > div.vboxChooserGroupVMs table.vboxChooserVM div.vboxVMName { position: relative; left: -20px; }'; + // Icon moves down + styleRules[styleRules.length] = 'div.vboxChooserGroup > div.vboxChooserGroupVMs table.vboxChooserVM img.vboxVMIcon { position: relative; top: 8px; }'; + // State text goes away + styleRules[styleRules.length] = 'div.vboxChooserGroup > div.vboxChooserGroupVMs table.vboxChooserVM span.vboxVMState { display: none; }'; + // Less padding + styleRules[styleRules.length] = 'div.vboxChooserGroup > div.vboxChooserGroupVMs table.vboxChooserVM td { padding: 0px; }'; + // Some group header items and drop targets go away + styleRules[styleRules.length] = 'div.vboxChooserGroup > div.vboxChooserGroupHeader > .vboxChooserGroupNameArrowCollapse, #' +vboxChooser._anchorid + ' div.vboxChooserGroup .vboxChooserDropTarget { display: none; }'; + styleRules[styleRules.length] = 'div.vboxChooserGroup { overflow: hidden; }'; + // host + styleRules[styleRules.length] = '#vboxChooserVMHost .vboxVMState { display: none; }'; + // group header + styleRules[styleRules.length] = 'div.vboxChooserGroup div.vboxChooserGroupHeader { height: auto; padding: 2px; }'; + + } + $('head').append('<style type="text/css" id="vboxChooserStyle">#'+vboxChooser._anchorid + ' ' + styleRules.join("\n#"+vboxChooser._anchorid + " ") + '</style>'); + + }, + + /* + * Get group element by path + */ + getGroupElement : function(gpath, noCreate) { + + if(!gpath) gpath = '/'; + var gnames = gpath.split('/'); + var groot = vboxChooser._anchor.children('div.vboxChooserGroup:not(.ui-draggable-dragging)'); + for(var i = 1; i < gnames.length; i++) { + + if(!gnames[i]) continue; + + var group = groot.children('div.vboxChooserGroup:not(.ui-draggable-dragging)').children('div.vboxChooserGroupIdentifier[title="'+gnames[i]+'"]').parent(); + + // If it does not exist, create it + if(!group[0]) { + + if(noCreate) return null; + + var gpath = '/'; + for(var a = 1; a <= i; a++) { + gpath = gpath + '/' + gnames[a]; + } + gpath = gpath.replace('//','/'); + + vboxChooser.groupHTML(gpath).insertBefore(groot.children('div.vboxChooserGroupVMs')); + + vboxChooser.sortGroup(groot); + + // Resize chooser elements + vboxChooser._initialResize = true; + vboxChooser._resizeElements(); + + groot = groot.children('div.vboxChooserGroup:not(.ui-draggable-dragging)').children('div.vboxChooserGroupIdentifier[title="'+gnames[i]+'"]').parent(); + + } else { + groot = group; + } + + } + return groot; + }, + + /* + * + * Update VM elements + * + */ + updateVMElement : function(vmUpdate, newVM) { + + // Not running.. don't do anything + if(!vboxChooser._running) return; + + // Stale event after vm was removed + if(!vmUpdate) return; + + // New VM + if(newVM) { + + // New VM.. add it to groups.. + if(!vmUpdate.groups || vmUpdate.groups.length == 0) + vmUpdate.groups = ['/']; + + for(var i = 0; i < vmUpdate.groups.length; i++) { + var gElm = $(vboxChooser.getGroupElement(vmUpdate.groups[i])); + vboxChooser.vmHTML(vmUpdate).appendTo( + gElm.children('div.vboxChooserGroupVMs') + ); + } + + // Existing VM. Replace existing elements + } else { + + $('#'+vboxChooser._anchorid).find('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmUpdate.id).each(function(i,elm){ + + var newHTML = vboxChooser.vmHTML(vmUpdate); + if($(elm).hasClass('vboxListItemSelected')) { + $(newHTML).addClass('vboxListItemSelected').removeClass('vboxHover'); + } + $(elm).children().replaceWith(newHTML.children()); + }); + + } + + }, + + + /* + * Returns true if there are VMs with ID vmid that are not selected + */ + vmHasUnselectedCopy : function (vmid) { + return ($(vboxChooser._anchor).find('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmid+':not(.vboxListItemSelected)').length > 0); + }, + + /* + * Remove selected VMs from the list and rewrite group definitions + * this assumes that there are other copies of these VMs that are not + * selected. + */ + removeVMs : function(vmids) { + + for(var i = 0; i < vmids.length; i++) { + $(vboxChooser._anchor).find('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmids[i]+'.vboxListItemSelected').remove(); + } + + // Update selection list + vboxChooser._selectedList = vboxChooser._selectedList.filter(function(v){ + return (v.type == 'group' || (jQuery.inArray(v.id, vmids) == -1)); + }); + + // Tell interface that selection list has changed + vboxChooser.selectionListChanged(vboxChooser._selectedList); + + // compose and save group definitions + vboxChooser.composeGroupDef(true); + + + // Possible resize needed + vboxChooser._resizeElements(true); + + }, + + /* + * Generate HTML from VM definition + */ + vmHTML : function (vmn) { + + var tbl = $('<table />').attr({'class':'vboxChooserItem-'+vboxChooser._anchorid+'-'+vmn.id + " vboxChooserVM"}) + .on('mousedown',vboxChooser.selectItem) + .hoverClass('vboxHover').data('vmid',vmn.id); + + + // Drag-and-drop functionality + ///////////////////////////////// + if(vmn.id != 'host' && $('#vboxPane').data('vboxSession').admin) { + + $(tbl).draggable({'cursorAt':{left: -10, top: -10},'helper':function(){ + return $(this).clone().css({'width':($(this).width()+2)+'px','display':'inline','background':'#fff','border-color':'#69f'}).removeClass('vboxHover'); + + // drag start + },'start':function(e) { + + if(!vboxChooser._editable) return false; + + $(vboxChooser._anchor).disableSelection(); + vboxChooser._dragging = vmn.id; + $(vboxChooser._anchor).find('table.vboxHover').removeClass('vboxHover'); + + // drag stop + },'stop':function(e) { + vboxChooser.vmDropped(e, $(this)); + }}); + } + + // Functionality to drop above / below VM + ///////////////////////////////////////////// + var td = $('<td />').attr({'colspan':'2'}).addClass('vboxChooserDropTarget vboxDropTargetTop'); + if(vmn.id != 'host') { + td.hover(function(){ + if(vboxChooser._dragging && vboxChooser._dragging != vmn.id) + $(this).addClass('vboxChooserDropTargetHover'); + },function(){ + $(this).removeClass('vboxChooserDropTargetHover'); + } + ); + } + $('<tr />').append(td).appendTo(tbl); + + + + // VM OS type icon + var tr = $('<tr />'); + if($('#vboxPane').data('vboxConfig').enableCustomIcons && vmn.customIcon) { + $('<td />').attr({'rowspan':'2'}).html("<img src='" + vmn.customIcon + "' class='vboxVMIcon' />").appendTo(tr); + } else { + $('<td />').attr({'rowspan':'2'}).html("<img src='images/vbox/" + vboxGuestOSTypeIcon(vmn.OSTypeId) + "' class='vboxVMIcon" + (vmn.id == 'host' ? " vboxHostIcon" : "") + "' />").appendTo(tr); + } + + + // VM Name + var td = $('<td />').attr({'class':'vboxVMTitle'}); + + // Host will have HTML in name and unique id + if(vmn.id == 'host') { + + $(tbl).attr('id', 'vboxChooserVMHost'); + + // Check for multiple server config + if($('#vboxPane').data('vboxConfig').servers.length) { + + // If there are multiple servers configured, setup menu + if(!$('#vboxServerMenu')[0]) { + var servers = $('#vboxPane').data('vboxConfig').servers; + var ul = $('<ul />').attr({'id':'vboxServerMenu','style':'display: none','class':'contextMenu'}); + for(var i = 0; i < servers.length; i++) { + $('<li />').html("<a href='#" + $('<div />').html(servers[i].name).text() + "' style='background-image: url(images/vbox/OSE/VirtualBox_16px.png);'>"+$('<div />').html(servers[i].name).text()+"</a>").appendTo(ul); + } + $('#vboxPane').append(ul); + } + + var span = $('<span />').attr({'class':'vboxServerLink'}).text('('+$('#vboxPane').data('vboxConfig').name+')').contextMenu({ + menu: 'vboxServerMenu', + button: 0, + mode: 'menu' + }, + function(a) { + + if(a == $('#vboxPane').data('vboxConfig').name) return; + + // Show loading screen + var l = new vboxLoader(); + l.showLoading(); + + // Empty selection list + vboxChooser.selectionListChanged(); + + + // Unsubscribe from events + $.when(vboxEventListener.stop()).done(function() { + + // Expire data mediator data + vboxVMDataMediator.expireAll(); + + // Trigger host change + vboxSetCookie("vboxServer",a); + $('#vboxPane').trigger('hostChange',[a]); + + }).always(function(){ + + // remove loading screen + l.removeLoading(); + + }); + + + } + ); + $(td).html('<span class="vboxVMName">VirtualBox</span> ').append(span); + } else { + $(td).html('<span class="vboxVMName">VirtualBox</span> ('+vmn.name+')'); + } + + // Not rendering host + } else { + + $(td).append('<div class="vboxFitToContainer vboxVMName"><span class="vboxVMName">'+$('<span />').text(vmn.name).html()+'</span>'+ (vmn.currentSnapshotName ? '<span class="vboxVMChooserSnapshotName"> (' + $('<span />').text(vmn.currentSnapshotName).html() + ')</span>' : '')+'</div>'); + + + // Table gets tool tips + tip = trans(vboxChooser._vmToolTip, 'UIVMListView').replace('%1',('<b>'+$('<span />') + .text(vmn.name).html()+'</b>'+(vmn.currentSnapshotName ? ' (' + $('<span />') + .text(vmn.currentSnapshotName).html() + ')' : ''))) + .replace('%2',trans(vboxVMStates.convert(vmn.state),'VBoxGlobal')) + .replace('%3',vboxDateTimeString(vmn.lastStateChange)) + .replace('%4',trans(vmn.sessionState,'VBoxGlobal').toLowerCase()); + + $(tbl).tipped({'source':tip,'position':'mouse','delay':1500}); + } + + $(tr).append(td).appendTo(tbl); + + // VM state row + var tr = $('<tr />'); + var td = $('<td />').attr({'class':(vmn.id != 'host' && vmn.sessionState != 'Unlocked' ? 'vboxVMSessionOpen' : '')}); + + // Add VirtualBox version if hosting + if(vmn.id == 'host') { + + $(td).html("<div class='vboxFitToContainer vboxVMState'><img src='images/vbox/" + vboxMachineStateIcon(vmn.state) +"' /><span class='vboxVMState'>" + trans(vboxVMStates.convert(vmn.state),'VBoxGlobal') + ' - ' + $('#vboxPane').data('vboxConfig').version.string+'</span></div>'); + + // Check for version mismatches? + if(!vboxChooser._versionChecked) { + vboxChooser._versionChecked = true; + var vStr = $('#vboxPane').data('vboxConfig').phpvboxver.substring(0,$('#vboxPane').data('vboxConfig').phpvboxver.indexOf('-')); + var vers = $('#vboxPane').data('vboxConfig').version.string.replace('_OSE','').split('.'); + if(vers[0]+'.'+vers[1] != vStr) { + vboxAlert('This version of phpVirtualBox ('+$('#vboxPane').data('vboxConfig').phpvboxver+') is incompatible with VirtualBox ' + $('#vboxPane').data('vboxConfig').version.string + ". You probably need to <a href='http://sourceforge.net/projects/phpvirtualbox/files/' target=_blank>download the latest phpVirtualBox " + vers[0]+'.'+vers[1] + "-x</a>.<p>See the Versioning section below the file list in the link for more information</p>",{'width':'auto'}); + } + } + } else { + $(td).html("<div class='vboxFitToContainer vboxVMState'><img src='images/vbox/" + vboxMachineStateIcon(vmn.state) +"' /><span class='vboxVMState'>" + trans(vboxVMStates.convert(vmn.state),'VBoxGlobal') + '</span></div>'); + } + + $(tr).append(td).appendTo(tbl); + + // Droppable targets + var td = $('<td />').attr({'colspan':'2'}).addClass('vboxChooserDropTarget vboxDropTargetBottom'); + if(vmn.id != 'host') { + td.hover(function(){ + if(vboxChooser._dragging && vboxChooser._dragging != vmn.id) + $(this).addClass('vboxChooserDropTargetHover'); + },function(){ + $(this).removeClass('vboxChooserDropTargetHover'); + } + ); + } + $('<tr />').addClass('vboxChooserDropTarget').css({'height':'4px'}).append(td).appendTo(tbl); + + + // Context menus? + if(vboxChooser._vmContextMenuObj) { + + $(tbl).contextMenu({ + menu: vboxChooser._vmContextMenuObj.menuId(), + menusetup : function(el) { + if(!$(el).hasClass('vboxListItemSelected')) $(el).trigger('click'); + } + },function(act,el,pos,d,e){ + vboxChooser._vmContextMenuObj.menuClickCallback(act); + }); + + // Open settings on dblclick + $(tbl).dblclick(function(){ + if(vboxChooser._vmContextMenuObj.menuItems['settings'].enabled()) + vboxChooser._vmContextMenuObj.menuItems['settings'].click(); + }); + } + + return tbl; + + }, + + + /* + * VM Group Dropped + */ + vmGroupDropped : function(e, droppedGroup) { + + + $(vboxChooser._anchor).enableSelection(); + + + var vmGroupPath = vboxChooser._draggingGroup; + vboxChooser._draggingGroup = false; + $(droppedGroup).removeClass('vboxHover'); + + if(!vboxChooser._editable) return false; + + // Cannot drag a group that contains a VM without + // an unlocked session state if it will modify VM + // Groups + var sessionLocked = false; + if($(droppedGroup).find('td.vboxVMSessionOpen')[0]) + sessionLocked=true; + + + // Check for above/below group first + var dropTarget = vboxChooser._anchor.find('div.vboxChooserDropTargetHover').first(); + if(dropTarget[0]) { + + // Make sure that this wasn't dropped onto a sub-group or itself + if( + !dropTarget.closest('div.vboxChooserGroup')[0] + || + vmGroupPath == dropTarget.closest('div.vboxChooserGroup').data('vmGroupPath') + || + dropTarget.closest('div.vboxChooserGroup').data('vmGroupPath').indexOf(vmGroupPath + '/') == 0 + ) { + return; + } + + + // If we are not still in the same group, check for name conflict + var currParentGroupPath = $(droppedGroup).closest('div.vboxChooserGroup').parent().closest('div.vboxChooserGroup').data('vmGroupPath'); + + if(dropTarget.closest('div.vboxChooserGroup').parent().closest('div.vboxChooserGroup').data('vmGroupPath') != currParentGroupPath) { + + // Do not allow to be dragged into another group + // if there is a Vm with a locked session in this one + if(sessionLocked && !$('#vboxPane').data('vboxConfig')['phpVboxGroups']) return; + + // Make sure there are no conflicts + var groupName = $(droppedGroup).children('div.vboxChooserGroupIdentifier').attr('title'); + var newGroupName = groupName; + + + var i = 2; + while(vboxChooser.groupNameConflicts(dropTarget.closest('div.vboxChooserGroup').parent(), newGroupName)) { + newGroupName = groupName + ' (' + (i++) + ')'; + } + + $(droppedGroup).children('div.vboxChooserGroupIdentifier').attr({'title':newGroupName}) + .siblings('div.vboxChooserGroupHeader') + .children('span.vboxChooserGroupName').text(newGroupName); + + } + + // Insert before or insert after? + if(dropTarget.hasClass('vboxDropTargetTop')) { + $(droppedGroup).detach().insertBefore(dropTarget.closest('div.vboxChooserGroup')); + } else { + $(droppedGroup).detach().insertAfter(dropTarget.closest('div.vboxChooserGroup')); + } + + + // Dropped onto a group or main VM list + } else { + + // Will not do this if this group contains + // a VM with a locked session + if(sessionLocked && !$('#vboxPane').data('vboxConfig')['phpVboxGroups']) return; + + var dropTarget = vboxChooser._anchor.find('div.vboxHover').first(); + + + // Dropped onto a group + if(dropTarget[0] && dropTarget.parent().hasClass('vboxChooserGroup')) { + + dropTarget = dropTarget.parent(); + + // Make sure that this wasn't dropped onto a sub-group or itself + if( + vmGroupPath == dropTarget.data('vmGroupPath') + || + dropTarget.closest('div.vboxChooserGroup').data('vmGroupPath').indexOf(vmGroupPath + '/') == 0 + ) { + return; + } + + // Dropped onto main vm list + } else if($(vboxChooser._anchor).find('div.vboxGroupHover').length == 0 && $(vboxChooser._anchor).hasClass('vboxChooserDropTargetHoverRoot')) { + + dropTarget = null; + + // Only showing one group? + if(vboxChooser._showOnlyGroupHistory.length > 0) { + dropTarget = $(vboxChooser._showOnlyGroupHistory[vboxChooser._showOnlyGroupHistory.length-1]); + } + + if(!$(dropTarget)[0]) + dropTarget = vboxChooser._anchor.children('div.vboxChooserGroup'); + + } else { + return; + } + + // Make sure there are no conflicts + var newElm = $(droppedGroup).detach(); + var groupName = $(droppedGroup).children('div.vboxChooserGroupIdentifier').attr('title'); + var newGroupName = groupName; + + var i = 2; + while(vboxChooser.groupNameConflicts(dropTarget, newGroupName, $(newElm).data('vmGroupPath'))) { + newGroupName = groupName + ' (' + (i++) + ')'; + } + + $(newElm) + .children('div.vboxChooserGroupIdentifier').attr({'title':newGroupName}) + .siblings('div.vboxChooserGroupHeader') + .children('span.vboxChooserGroupName').text(newGroupName); + $(newElm).insertBefore(dropTarget.children('div.vboxChooserGroupVMs')); + + } + + // vmGroup dropped - compose and save group definitions + vboxChooser.composeGroupDef(true); + + // Hide group info + vboxChooser._anchor.find('div.vboxChooserGroupHeader').trigger('mouseout'); + + // Resize chooser elements + vboxChooser._resizeElements(); + + vboxChooser.selectionListChanged(vboxChooser._selectedList); + + }, + + /* + * VM dropped + */ + vmDropped : function (e, droppedVM){ + + + $(vboxChooser._anchor).enableSelection(); + vboxChooser._dragging = null; + + if(!vboxChooser._editable) return false; + + // Cannot drag if this VM's session is not open + var thisSessionLocked = false; + var vmData = vboxVMDataMediator.getVMData($(droppedVM).data('vmid')); + + if(vmData.sessionState != 'Unlocked') + thisSessionLocked = true; + + + + // Where was this dropped? + var dropTarget = $('#'+vboxChooser._anchorid).find('td.vboxChooserDropTargetHover'); + + // Dropped above / below a VM + if(dropTarget[0]) { + + // Dropped from another group into this one, + // but this group already has this VM + if((dropTarget.closest('table').closest('div.vboxChooserGroup').data('vmGroupPath') != $(droppedVM).closest('div.vboxChooserGroup').data('vmGroupPath')) + && dropTarget.closest('table').siblings('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+$(droppedVM).data('vmid'))[0]) { + return true; + } + + // If session of this VM is locked, don't allow it to be + // dragged out of current group + if(thisSessionLocked && !$('#vboxPane').data('vboxConfig')['phpVboxGroups'] && ($(droppedVM).closest('div.vboxChooserGroup').data('vmGroupPath') != dropTarget.closest('div.vboxChooserGroup').data('vmGroupPath'))) { + return + } + + // Get VM from target's parent table + if(dropTarget.hasClass('vboxDropTargetTop')) { + if(!e.ctrlKey && !e.metaKey) { + $(droppedVM).detach().insertBefore($(dropTarget).closest('table')); + } else { + // Copy + if($(dropTarget).closest('table').parent().children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmData.id)[0]) + return; + vboxChooser.vmHTML(vmData).insertBefore($(dropTarget).closest('table')); + } + } else { + if(!e.ctrlKey && !e.metaKey) { + $(droppedVM).detach().insertAfter($(dropTarget).closest('table')); + } else { + // Copy - Don't allow if it already exists + if($(dropTarget).closest('table').parent().children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmData.id)[0]) + return; + vboxChooser.vmHTML(vmData).insertAfter($(dropTarget).closest('table')); + } + } + + // Not dropped above / below vm + } else { + + // Don't allow this if sessoin is locked + if(thisSessionLocked && !$('#vboxPane').data('vboxConfig')['phpVboxGroups']) return; + + // Dropped ON a vm? + dropTarget = $('#'+vboxChooser._anchorid).find('table.vboxHover:not(.ui-draggable-dragging)').first(); + if($(dropTarget).data('vmid')) { + + // Create a group? + dropTarget = $('#'+vboxChooser._anchorid).find('table.vboxHover').first(); + + // Nothing to do. Not dropped on valid target + if(!dropTarget[0] || ($(dropTarget).data('vmid') == $(droppedVM).data('vmid'))) return true; + + // Dont' allow this if target VM's session is locked + if($(dropTarget).find('td.vboxVMSessionOpen')[0]) + return; + + // Where to drop vboxChooser.. + var p = dropTarget.closest('div.vboxChooserGroup').children('div.vboxChooserGroupVMs'); + // assume root? + if(!p[0]) p = vboxChooser._anchor.children('div.vboxChooserGroupVMs'); + + // Determine group name + var gname = trans('New group','UIGChooserModel'); + var tgname = gname; + + var i = 2; + while(vboxChooser.groupNameConflicts($(p).parent(), tgname)) { + tgname = gname + ' ' + (i++); + } + + + // New position is below target + var ghtml = vboxChooser.groupHTML(String(dropTarget.closest('div.vboxChooserGroup').data('vmGroupPath')+'/'+tgname).replace('//','/')); + + if(!e.ctrlKey && !e.metaKey) { + ghtml.children('div.vboxChooserGroupVMs').append($(droppedVM).detach()); + } else { + ghtml.children('div.vboxChooserGroupVMs').append(vboxChooser.vmHTML(vmData)); + } + ghtml.children('div.vboxChooserGroupVMs').append(dropTarget.detach()); + + ghtml.insertBefore(p); + + // Dropped in the main VM list or group header? + } else { + + dropTarget = $(vboxChooser._anchor).find('div.vboxHover').first(); + if(dropTarget[0] && dropTarget.hasClass('vboxChooserGroupHeader')) { + + // Group already has this dragging VM? + if(dropTarget.siblings('div.vboxChooserGroupVMs').children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+$(droppedVM).data('vmid'))[0]) { + return; + } + + if(!e.ctrlKey && !e.metaKey) + $(droppedVM).detach().appendTo(dropTarget.siblings('div.vboxChooserGroupVMs').first()); + else + vboxChooser.vmHTML(vmData).appendTo(dropTarget.siblings('div.vboxChooserGroupVMs').first()); + + // Main VM list + } else if($(vboxChooser._anchor).find('div.vboxGroupHover').length == 0 && $(vboxChooser._anchor).hasClass('vboxChooserDropTargetHoverRoot')) { + + dropTarget = null; + + // Only showing one group? + if(vboxChooser._showOnlyGroupHistory.length > 0) { + dropTarget = $(vboxChooser._showOnlyGroupHistory[vboxChooser._showOnlyGroupHistory.length-1]); + } + + if(!$(dropTarget)[0]) + dropTarget = vboxChooser._anchor.children('div.vboxChooserGroup'); + + // Already in this list? + if(dropTarget.children('div.vboxChooserGroupVMs').children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+$(droppedVM).data('vmid'))[0]) { + return true; + } + + if(!e.ctrlKey && !e.metaKey) { + $(droppedVM).detach().appendTo(dropTarget.children('div.vboxChooserGroupVMs').first()); + } else { + vboxChooser.vmHTML(vmData).appendTo(dropTarget.children('div.vboxChooserGroupVMs').first()); + } + + } + } + + } + + // vm dropped - compose and save group definitions + vboxChooser.composeGroupDef(true); + + // Resize chooser elements + vboxChooser._resizeElements(); + + vboxChooser.selectionListChanged(vboxChooser._selectedList); + + }, + + /* + * Group selected items into a new group + */ + groupSelectedItems : function() { + + // Get all group paths to determine new group target + var groupPaths = {}; + vboxChooser._anchor.find('div.vboxVMGroupSelected').closest('div.vboxChooserGroup').each(function(idx,elm) { + groupPaths[$(elm).data('vmGroupPath')] = 1; + }); + vboxChooser._anchor.find('table.vboxListItemSelected').closest('div.vboxChooserGroup').each(function(idx,elm) { + groupPaths[$(elm).data('vmGroupPath')] = 1; + }); + + // The group clsest to the root group will be the target + var groupPathTarget = null; + for(var i in groupPaths) { + + if(typeof(i) != 'string') continue; + + // Already at root group. Nothing to do + if(groupPathTarget == '/') break; + + // No target set yet or equal targets, or this group is the root + if(!groupPathTarget || groupPathTarget == i || i == '/') { + groupPathTarget = i; + continue; + } + + var t1 = groupPathTarget.split("/"); + var t2 = i.split("/"); + for(var i = 0; i < Math.min(t1.length,t2.length); i++) { + if(t1[i] != t2[i]) { + groupPathTarget = ''; + for(var a = 0; a < i; a++) { + groupPathTarget += "/" + t1[a]; + } + groupPathTarget = groupPathTarget.replace('//','/'); + break; + } + } + + + } + + var target = vboxChooser.getGroupElement(groupPathTarget, true); + + if(!$(target)[0]) return; + + // Determine group name + var gname = trans('New group','UIGChooserModel'); + var tgname = gname; + + var i = 2; + while(vboxChooser.groupNameConflicts($(target), tgname)) { + tgname = gname + ' ' + (i++); + } + + var gHTML = vboxChooser.groupHTML('/'+tgname); + + // Append group and vm elements + vboxChooser._anchor.find('div.vboxVMGroupSelected').detach().insertAfter(gHTML.children('div.vboxChooserGroupHeader')); + vboxChooser._anchor.find('table.vboxListItemSelected').detach().appendTo(gHTML.children('div.vboxChooserGroupVMs')); + + gHTML.insertBefore($(target).children('div.vboxChooserGroupVMs')); + + // group selected items, + // Compose and save group definitions + vboxChooser.composeGroupDef(true); + + // Resize chooser elements + vboxChooser._resizeElements(); + + + + }, + + /** + * Compose group data from GUI and optionally save it + * + * @param save - save group definitions to vbox + */ + composeGroupDef : function(save) { + + var allGroups = []; + var groupsResolved = false; + + // Keep looping through group definitions until + // there are no groups removed + while(!groupsResolved) { + + allGroups = []; + groupsResolved = true; + + vboxChooser._anchor.find('div.vboxChooserGroup:not(.ui-draggable-dragging)').each(function(idx,elm) { + + // Group element was removed + if(!$(elm)[0]) return; + + // Compose group path + var myPath = $(elm).children('div.vboxChooserGroupIdentifier').attr('title'); + if(!myPath) myPath = '/'; + $(elm).parents('div.vboxChooserGroup:not(.ui-draggable-dragging)').each(function(idx2,elm2){ + var pName = $(elm2).children('div.vboxChooserGroupIdentifier').attr('title'); + if(!pName) pName = '/'; + myPath = String(pName + '/' + myPath).replace('//','/'); + }); + + // Groups + var gList = []; + $(elm).children('div.vboxChooserGroup:not(.ui-draggable-dragging)').each(function(idx2,elm2){ + + // If this group is selected, we'll have to update its path + // in the selection list + var selected = $(elm2).hasClass('vboxVMGroupSelected'); + var oldPath = $(elm2).data('vmGroupPath'); + var newPath = String(myPath + '/' + $(elm2).children('div.vboxChooserGroupIdentifier').attr('title')).replace('//','/'); + + gList[gList.length] = $(elm2).children('div.vboxChooserGroupIdentifier').attr('title'); + + // set / correct group path data + $(elm2).data('vmGroupPath', newPath); + + // Group's path changed? + if(selected && (oldPath != newPath)) { + for(var i = 0; i < vboxChooser._selectedList.length; i++) { + if(vboxChooser._selectedList[i].type == 'group' && vboxChooser._selectedList[i].groupPath == oldPath) { + vboxChooser._selectedList[i].groupPath = String(myPath + '/' + $(elm2).children('div.vboxChooserGroupIdentifier').attr('title')).replace('//','/'); + break; + } + } + } + + }); + + // VMs + var vmList = []; + $(elm).children('div.vboxChooserGroupVMs').children('table.vboxChooserVM:not(.ui-draggable-dragging)').each(function(idx3,elm3){ + vmList[vmList.length] = $(elm3).data('vmid'); + }); + + // Skip and remove if there are no VMs or subgroups + // And it is not the parent group + if(gList.length + vmList.length == 0 && !$(elm).hasClass('vboxChooserGroupRoot')) { + + // remove from selected list? + if(elm && $(elm).hasClass('vboxVMGroupSelected')) { + + var myPath = $(elm).data('vmGroupPath'); + // Deselect item + vboxChooser._selectedList = vboxChooser._selectedList.filter(function(v){ + return (v.type != 'group' || (v.groupPath != myPath)); + }); + } + $(elm).empty().remove(); + groupsResolved = false; + return false; + } + + // append to all groups list + gorder = []; + if(gList.length) gorder[0] = 'go='+gList.join(',go='); + if(vmList.length) gorder[gorder.length] = 'm='+vmList.join(',m='); + allGroups[allGroups.length] = { + path: $(elm).data('vmGroupPath'), + order: gorder.join(',') + }; + + // Update counts span + $(elm).children('div.vboxChooserGroupVMs').css({'display':(vmList.length || $(elm).data('vmGroupPath') == '/' ? '' : 'none')}) + .siblings('div.vboxChooserGroupHeader') + .each(function(hidx,header) { + + var staticTip = '<strong>'+$(header).siblings('div.vboxChooserGroupIdentifier').attr('title')+'</strong>'+ + (gList.length ? ('<br />' + trans('%n group(s)','UIGChooserItemGroup',gList.length).replace('%n',gList.length)) : '') + + (vmList.length ? ('<br />' + trans('%n machine(s)','UIGChooserItemGroup',vmList.length).replace('%n',vmList.length)) : ''); + + $(header).tipped({'source':function() { + + // find number of running VMs + var runningVMs = 0; + + if(vmList.length) { + $(header).siblings('div.vboxChooserGroupVMs').find('td.vboxVMSessionOpen').each(function(idx,elm3) { + if(vboxVMStates.isRunning(vboxVMDataMediator.getVMData($(elm3).closest('table').data('vmid')))) + runningVMs++; + }); + } + + return staticTip + (runningVMs > 0 ? ' ' + trans('(%n running)','UIGChooserItemGroup',runningVMs).replace('%n', runningVMs) : ''); + } + ,'position':'mouse','delay':1500}); + }) + .children('span.vboxChooserGroupInfo') + .children('span.vboxChooserGroupCounts').html( + (gList.length ? ('<span style="background-image:url(images/vbox/group_abstract_16px.png);" />'+gList.length) : '') + + (vmList.length ? ('<span style="background-image:url(images/vbox/machine_abstract_16px.png);" />'+vmList.length) : '') + ); + }); + + } + + // Save GUI group definition? + if(!save) return; + + + // Tell the interface we're about to save groups + vboxChooser._editable = false; + $('#vboxPane').trigger('vmGroupDefsSaving'); + + vboxChooser._groupDefs = allGroups; + + // Save machine groups and trigger change + var vms = []; + var vmList = vboxVMDataMediator.getVMList(); + for(var i = 0; i < vmList.length; i++) { + + if(!vmList[i] || vmList[i].id == 'host') continue; + + /* If a VM's groups have changed, add it to the list */ + var eGroups = vmList[i].groups; + var nGroups = vboxChooser.getGroupsForVM(vmList[i].id); + + if($(nGroups).not(eGroups).length || $(eGroups).not(nGroups).length) { + + vms[vms.length] = { + 'id' : vmList[i].id, + 'groups' : nGroups + }; + } + } + + // Save machines groups? + if(vms.length) { + + // Reload VMs and group definitions + var reloadAll = function() { + + var ml = new vboxLoader(); + ml.add('vboxGetMedia',function(d){$('#vboxPane').data('vboxMedia',d.responseData);}); + ml.add('vboxGroupDefinitionsGet',function(d){vboxChooser._groupDefs = d.responseData;}); + + // Reload VM list and group definitions, something went wrong + ml.onLoad = function() { + + // Stop vmlist from refreshing.. + vboxChooser.stop(); + + // reset selections + $('#vboxPane').trigger('vmSelectionListChanged',[vboxChooser]); + + // ask for new one + vboxChooser.start(); + }; + ml.run(); + }; + + $.when(vboxAjaxRequest('machinesSaveGroups',{'vms':vms})).done(function(res){ + + if(res.responseData.errored) { + reloadAll(); + vboxChooser._editable = true; + $('#vboxPane').trigger('vmGroupDefsSaved'); + } else { + $.when(vboxAjaxRequest('vboxGroupDefinitionsSet',{'groupDefinitions':allGroups})).always(function(){ + vboxChooser._editable = true; + $('#vboxPane').trigger('vmGroupDefsSaved'); + }); + } + + }).fail(reloadAll); + + } else { + $.when(vboxAjaxRequest('vboxGroupDefinitionsSet',{'groupDefinitions':allGroups})).always(function(){ + vboxChooser._editable = true; + $('#vboxPane').trigger('vmGroupDefsSaved'); + }); + } + + + return allGroups; + + }, + + /* + * Return a list of groups that VM is a member of + */ + getGroupsForVM : function(vmid) { + var gPathList = []; + vboxChooser._anchor.find('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmid+':not(.ui-draggable-dragging)').each(function(idx,elm){ + var gParent = $(elm).closest('div.vboxChooserGroup'); + if(!gParent.hasClass('ui-draggable-dragging')) + gPathList[gPathList.length] = gParent.data('vmGroupPath'); + }); + return gPathList; + }, + + /* + * Determine whether or not a group name conflicts + * with another group in parent + */ + groupNameConflicts : function(parentGroup, name, ignoreGroupAtPath) { + var found = false; + parentGroup.children('div.vboxChooserGroup:not(.ui-draggable-dragging)').children('div.vboxChooserGroupIdentifier[title="'+name+'"]').parent().each(function(i,elm){ + + if(ignoreGroupAtPath && (ignoreGroupAtPath == $(elm).data('vmGroupPath'))) + return true; + + found=true; + return false; + }); + return found; + }, + + /* + * Ungroup selected group + */ + unGroupSelectedGroup : function() { + + var target = $(vboxChooser.getSelectedGroupElements()[0]).siblings('div.vboxChooserGroupVMs'); + + // Groups + // - ignore group at path we are currently ungrouping + var ignoreGroup = $(vboxChooser.getSelectedGroupElements()[0]).data('vmGroupPath'); + $(vboxChooser.getSelectedGroupElements()[0]).children('div.vboxChooserGroup').each(function(i,elm) { + + // Make sure there are no conflicts + var newElm = $(elm).detach(); + var groupName = $(elm).children('div.vboxChooserGroupIdentifier').attr('title'); + var newGroupName = groupName; + + var i = 2; + while(vboxChooser.groupNameConflicts($(target).parent(), newGroupName, ignoreGroup)) { + newGroupName = groupName + ' (' + (i++) + ')'; + } + + $(newElm).children('div.vboxChooserGroupIdentifier').attr({'title':newGroupName}) + .siblings('div.vboxChooserGroupHeader') + .children('span.vboxChooserGroupName').text(newGroupName); + + $(newElm).insertBefore(target); + + }); + + // VMs + $(vboxChooser.getSelectedGroupElements()[0]).children('div.vboxChooserGroupVMs').children().each(function(i,elm){ + $(elm).detach(); + if(!target.children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+$(elm).data('vmid'))[0]) + target.append(elm); + }); + + + // ungroup selected items + // compose and save group definitions + vboxChooser.composeGroupDef(true); + + // Resize chooser elements + vboxChooser._resizeElements(); + + vboxChooser.selectionListChanged(); + + + }, + + /* + * Sort group sub-elements based on VirtualBox + * group definitions + */ + sortGroup : function(gElm) { + + // Find group orders + var gPath = $(gElm).data('vmGroupPath'); + var groupList = vboxChooser._groupDefs; + + if(!(gPath && groupList)) return; + + var machineOrder = []; + var groupOrder = []; + + // Get correct order + for(var i = 0; i < groupList.length; i++) { + if(groupList[i].path == gPath) { + order = groupList[i].order.split(','); + for(var a = 0; a < order.length; a++) { + kv = order[a].split('=',2); + if(kv[0] == 'm') machineOrder[machineOrder.length] = kv[1]; + else groupOrder[groupOrder.length] = kv[1]; + } + } + } + + // sort groups + var groups = $(gElm).children('div.vboxChooserGroup').get(); + var maxPos = groups.length; + groups.sort(function(a,b){ + + var Pos1 = jQuery.inArray($(a).children('div.vboxChooserGroupIdentifier').attr('title'), groupOrder); + var Pos2 = jQuery.inArray($(b).children('div.vboxChooserGroupIdentifier').attr('title'), groupOrder); + + if(Pos1==-1) Pos1 = maxPos; + if(Pos2==-1) Pos2 = maxPos; + + return (Pos1 > Pos2 || Pos1 == -1 ? -1 : (Pos2 == Pos1 ? 0 : 1)); + + }); + $.each(groups, function(idx,itm) { + $(itm).insertAfter($(gElm).children('div.vboxChooserGroupHeader')); + }); + + // sort VMs + var vms = $(gElm).children('div.vboxChooserGroupVMs').children('table.vboxChooserVM').get(); + var maxPos = vms.length; + vms.sort(function(a,b) { + + var Pos1 = jQuery.inArray($(a).data('vmid'), machineOrder); + var Pos2 = jQuery.inArray($(b).data('vmid'), machineOrder); + + if(Pos1==-1) Pos1 = maxPos; + if(Pos2==-1) Pos2 = maxPos; + + return (Pos1 > Pos2 ? 1 : (Pos2 == Pos1 ? 0 : -1)); + + }); + $.each(vms, function(idx,itm) { + $(gElm).children('div.vboxChooserGroupVMs').append(itm); + }); + + + }, + + /* + * Sort selected group by item names + */ + sortSelectedGroup : function(rootElm) { + + var el = $(vboxChooser.getSelectedGroupElements()[0]); + + if(rootElm || !el) { + el = vboxChooser._anchor.children('div.vboxChooserGroup'); + } + + // sort groups + var groups = $(el).children('div.vboxChooserGroup').get(); + groups.sort(function(a,b){ + return $(b).children('div.vboxChooserGroupIdentifier').attr('title').localeCompare($(a).children('div.vboxChooserGroupIdentifier').attr('title')); + }); + $.each(groups, function(idx,itm) { + $(itm).insertAfter($(el).children('div.vboxChooserGroupHeader')); + }); + + // sort VMs + var vms = $(el).children('div.vboxChooserGroupVMs').children('table.vboxChooserVM').get(); + vms.sort(function(a,b) { + return $(a).find('span.vboxVMName').text().localeCompare($(b).find('span.vboxVMName').text()); + }); + $.each(vms, function(idx,itm) { + $(el).children('div.vboxChooserGroupVMs').append(itm); + }); + + // compose and save group definitions + vboxChooser.composeGroupDef(true); + + }, + + /* + * Rename selected group + */ + renameSelectedGroup : function() { + + var el = $(vboxChooser.getSelectedGroupElements()[0]); + + // Function to rename group + var renameGroup = function(e, textbox) { + + if(!textbox) textbox = $(this); + + var newName = $(textbox).val().replace(/[\\\/:*?"<>,]/g,'_'); + + if(newName && newName != $(textbox).closest('div.vboxChooserGroup').children('div.vboxChooserGroupIdentifier').attr('title')) { + + // Do not rename if it conflicts + var noConflict = newName; + var i = 2; + while(vboxChooser.groupNameConflicts($(textbox).parent().parent().parent().parent(), noConflict)) { + noConflict = newName + ' (' + (i++) + ')'; + } + newName = noConflict; + + $(textbox).closest('div.vboxChooserGroup') + .children('div.vboxChooserGroupIdentifier').attr({'title':newName}) + .siblings('div.vboxChooserGroupHeader') + .children('span.vboxChooserGroupName').html(newName); + + // group renamed, compose and save groups + vboxChooser.composeGroupDef(true); + + // Write out collapsed group list + vboxChooser._saveCollapsedGroups(); + + } + + + $(textbox).parent().parent().children().css({'display':''}); + $(textbox).parent().empty().remove(); + }; + + $(el).children('div.vboxChooserGroupHeader').children().css({'display':'none'}); + $(el).children('div.vboxChooserGroupHeader').append( + + $('<form />').append( + $('<input />').attr({'type':'text','value':$(el).children('div.vboxChooserGroupIdentifier').attr('title')}).css({'width':'90%','padding':'0px','margin':'0px'}).on('keypress',function(e){ + if (e.which == 13) { + $(this).off('blur', renameGroup); + renameGroup(e,this); + e.stopPropagation(); + e.preventDefault(); + $(this).trigger('blur'); + return false; + } + }) + ) + + ); + $(el).children('div.vboxChooserGroupHeader').children('form').children('input').focus().select().blur(renameGroup); + + }, + + /* + * Select a single group + */ + _selectGroup : function(gelm) { + $(gelm).addClass('vboxVMGroupSelected'); + }, + + /* + * Deselect a single group + */ + _deselectGroup : function(gelm) { + + $(gelm).removeClass('vboxVMGroupSelected'); + }, + + /* + * Select (or unselect) an item in our list. Called onmousedown or onCLick + */ + selectItem : function(e) { + + // Right click selects item if it is not selected + if(e.which != 1) { + + // Right click on group header and group is selected + // just return and show context menu + if($(this).hasClass('vboxChooserGroupHeader') && $(this).parent().hasClass('vboxVMGroupSelected')) { + return true; + + // Right click on VM and VM is already selected + // just return and show context menu + } else if($(this).hasClass('vboxListItemSelected')) { + return true; + } + } + + var selectedList = []; + var item = $(this); + + + // Group? + if($(item).hasClass('vboxChooserGroupHeader')) { + + + // No control key. Exclusive selection + if(!e.ctrlKey && !e.metaKey) { + + // already selected + if(vboxChooser._selectedList.length == 1 && vboxChooser._selectedList[0].type == 'group' && + vboxChooser._selectedList[0].groupPath == $(item).parent().data('vmGroupPath')) + return true; + + vboxChooser._anchor.find('.vboxListItemSelected').removeClass('vboxListItemSelected'); + vboxChooser._anchor.find('div.vboxVMGroupSelected') + .each(function(idx,gelm) { + vboxChooser._deselectGroup(gelm); + }); + + + + // select current group + vboxChooser._selectGroup($(item).parent()); + + selectedList = [{ + type: 'group', + groupPath: $(item).parent().data('vmGroupPath') + }]; + + // Already selected, and ctrl key + } else if($(item).parent().hasClass('vboxVMGroupSelected')){ + + // Deselect item + selectedList = vboxChooser._selectedList.filter(function(v){ + return (v.type != 'group' || (v.groupPath != $(item).parent().data('vmGroupPath'))); + }); + + vboxChooser._deselectGroup($(item).parent()); + + // Not already selected, and ctrl key + } else { + + vboxChooser._selectGroup($(item).parent()); + + selectedList = vboxChooser._selectedList; + + selectedList[selectedList.length] = { + type: 'group', + groupPath: $(item).parent().data('vmGroupPath') + }; + + } + + + // VM + } else { + + // No ctrl key or selection is host. Exclusive selection + if((!e.ctrlKey && !e.metaKey) || $(item).data('vmid') == 'host') { + + vboxChooser._anchor.find('.vboxListItemSelected').removeClass('vboxListItemSelected'); + vboxChooser._anchor.find('div.vboxVMGroupSelected').removeClass('vboxVMGroupSelected') + .each(function(idx,gelm){ + vboxChooser._deselectGroup(gelm); + }); + + // Select current VM + $(item).addClass('vboxListItemSelected').removeClass('vboxHover'); + + // already selected + if(vboxChooser._selectedList.length == 1 && vboxChooser._selectedList[0].type == 'vm' && + vboxChooser._selectedList[0].id == $(item).data('vmid')) + return true; + + selectedList = [{ + type: 'vm', + id: $(item).data('vmid'), + groupPath: $(item).parent().data('vmGroupPath') + }]; + + // Already selected, and ctrl key + } else if($(item).hasClass('vboxListItemSelected')) { + + // Deselect item + selectedList = vboxChooser._selectedList.filter(function(v){ + return (v.type == 'group' || (v.id != $(item).data('vmid'))); + }); + + $(item).removeClass('vboxListItemSelected'); + + // ctrl key, but not already selected + } else { + + $(item).addClass('vboxListItemSelected').removeClass('vboxHover'); + + selectedList = vboxChooser._selectedList; + + selectedList[selectedList.length] = { + type: 'vm', + id: $(item).data('vmid'), + groupPath: $(item).parent().data('vmGroupPath') + }; + + } + + } + + // Remove host? + if(selectedList.length > 1) { + + // Deselect host + selectedList = selectedList.filter(function(v){ + return (v.type == 'group' || (v.id != 'host')); + }); + + vboxChooser._anchor.children('table.vboxChooserItem-'+vboxChooser._anchorid+'-host').removeClass('vboxListItemSelected'); + + } + + vboxChooser.selectionListChanged(selectedList); + + return true; + + + }, + + /* + * Show only single group element identified by gelm + */ + showOnlyGroupElm : function(gelm) { + + // Going backwards affects animations + var back = false; + + // gelm is null if we're going backwards + if(!gelm) { + + + if(vboxChooser._showOnlyGroupHistory.length > 1) { + // this gets rid of current + vboxChooser._showOnlyGroupHistory.pop(); + // selects previous + gelm = vboxChooser._showOnlyGroupHistory.pop(); + back = true; + } else { + gelm = null; + } + + + } else { + + // Hold history + vboxChooser._showOnlyGroupHistory[vboxChooser._showOnlyGroupHistory.length] = gelm; + } + + + // No scrolling + vboxChooser._anchor.css({'overflow-y':'hidden'}); + + if($(gelm)[0]) { + + + // Slide over or back + $.when(vboxChooser._anchor.hide('slide', {direction: (back ? 'right' : 'left'), distance: (vboxChooser._anchor.outerWidth()/1.5)}, 200)).always(function() { + + + /* hide host when showing only a group */ + $('table.vboxChooserItem-'+vboxChooser._anchorid+'-host').hide(); + + + /* Undo anything previously performed by this */ + vboxChooser._anchor.find('div.vboxChooserGroupHide').removeClass('vboxChooserGroupHide vboxChooserGroupHideShowContainer'); + vboxChooser._anchor.find('div.vboxChooserGroupShowOnly').removeClass('vboxChooserGroupShowOnly'); + + $(gelm).parents('div.vboxChooserGroup').addClass('vboxChooserGroupHide vboxChooserGroupHideShowContainer').siblings().addClass('vboxChooserGroupHide'); + + vboxChooser._anchor.find('div.vboxChooserGroupRootLevel').removeClass('vboxChooserGroupRootLevel'); + $(gelm).addClass('vboxChooserGroupShowOnly vboxChooserGroupRootLevel').siblings().addClass('vboxChooserGroupHide'); + + $.when(vboxChooser._anchor.show('slide', {direction: (back ? 'left' : 'right'), distance: (vboxChooser._anchor.outerWidth()/1.5)}, 200)) + .done(function(){ + + // Restore scrolling + vboxChooser._anchor.css({'overflow-y':'auto'}); + + // Hide group info + $(gelm).find('div.vboxChooserGroupHeader').trigger('mouseout'); + + // Reset title sizes + vboxChooser._resizeElements(); + + // force redraw of these + $(gelm).find('.vboxFitToContainer').css({'display':'none'}).css({'width':'','display':''}); + + + }); + + }); + + } else { + + vboxChooser._showOnlyGroupHistory = []; + + // Slide back to anchor + $.when(vboxChooser._anchor.hide('slide', {direction: 'right', distance: (vboxChooser._anchor.outerWidth()/1.5)}, 200)).always(function() { + + /* show host when going back to main list */ + $('table.vboxChooserItem-'+vboxChooser._anchorid+'-host').show(); + + vboxChooser._anchor.find('div.vboxChooserGroupHide').removeClass('vboxChooserGroupHide vboxChooserGroupHideShowContainer'); + vboxChooser._anchor.find('div.vboxChooserGroupShowOnly').removeClass('vboxChooserGroupShowOnly '); + + vboxChooser._anchor.find('div.vboxChooserGroupRootLevel').removeClass('vboxChooserGroupRootLevel'); + vboxChooser._anchor.children('div.vboxChooserGroupRoot').addClass('vboxChooserGroupRootLevel'); + + $.when(vboxChooser._anchor.show('slide', {direction: 'left', distance: (vboxChooser._anchor.outerWidth()/1.5)}, 200)) + .done(function(){ + + // Restore scrolling + vboxChooser._anchor.css({'overflow-y':'auto'}); + + // Hide group info + vboxChooser._anchor.find('div.vboxChooserGroupHeader').trigger('mouseout'); + + // Reset title sizes + vboxChooser._resizeElements(); + + // force redraw of these + vboxChooser._anchor.find('.vboxFitToContainer').css({'display':'none','width':''}).css({'display':''}); + }); + }); + } + + }, + + /* + * Return HTML for group + */ + groupHTML : function(gpath) { + + if(!gpath) gpath = '/'; + var first = gpath == '/'; + var gname = gpath.substring(gpath.lastIndexOf('/')+1); + var collapsed = vboxChooser._isGroupCollapsed(gpath); + + var gHTML = $('<div />').append( + $('<div />').addClass('vboxChooserGroupIdentifier').css({'display':'none'}).attr({'title':gname}) + ).append( + $('<div />').addClass('vboxChooserGroupHeader').css({'display':(first ? 'none' : '')}) + .attr({'title':gname}) + .dblclick(function() { + + // Already collapsed? + var collapsed = $(this).closest('div.vboxChooserGroup').hasClass('vboxVMGroupCollapsed'); + + // Button rotation function + var rotateButton = function(){return true;}; + + var vboxArrowImage = $(this).find('span.vboxChooserGroupNameArrowCollapse'); + + if(!($.browser.msie && $.browser.version.substring(0,1) < 9)) { + + rotateButton = function() { + + return $('<div />').animate({left:90},{ + duration: 300, + step: function(currentStep) { + if(!collapsed) currentStep = (90 - currentStep); + vboxArrowImage.css({ + 'transform':'rotate('+currentStep+'deg)', + '-moz-transform': 'rotate('+currentStep+'deg)', + '-webkit-transform': 'rotate('+currentStep+'deg)', + '-o-transform': 'rotate('+currentStep+'deg)', + '-ms-transform': 'rotate('+currentStep+'deg)' + }); + }, + queue: true, + complete: function() { + vboxArrowImage.css({ + 'transform':'', + '-moz-transform': '', + '-webkit-transform': '', + '-o-transform': '', + '-ms-transform': '' + }); + } + + }); + }; + } + + + // Run button rotation and toggle class + $.when(rotateButton(), $(this).closest('div.vboxChooserGroup').toggleClass('vboxVMGroupCollapsed', ($.browser.msie && $.browser.version.substring(0,1) < 9) ? undefined : 300)).always(function(){ + + // Write out collapsed group list + vboxChooser._saveCollapsedGroups(); + + // Reset title sizes + vboxChooser._resizeElements(); + }); + + + }) + .append( + $('<div />').addClass('vboxChooserDropTarget') + .addClass('vboxDropTargetTop').hover(function(){ + if(vboxChooser._draggingGroup) + $(this).addClass('vboxChooserDropTargetHover' + (first ? 'ignore' : '')); + }, function(){ + $(this).removeClass('vboxChooserDropTargetHover'); + }) + ) + .append( + $('<span />').addClass('vboxChooserGroupNameArrowLeft vboxChooserGroupNameArrowCollapse vboxArrowImage') + .mousedown(function(e){ + e.stopPropagation(); + e.preventDefault(); + return false; + }).mouseup(function(){ + $(this).closest('div.vboxChooserGroupHeader').trigger('dblclick'); + }) + + ).append( + + $('<span />').addClass('vboxChooserGroupNameArrowLeft vboxChooserGroupShowOnlyBack vboxArrowImage') + .click(function(e) { + e.stopPropagation(); + e.preventDefault(); + vboxChooser.showOnlyGroupElm(); + return false; + + }) + + ) + .append($('<span />').addClass('vboxChooserGroupInfo').html( + "<span class='vboxChooserGroupCounts' />" + ).append( + $('<span />').addClass('vboxChooserGroupShowOnly vboxArrowImage') + .click(function(e){ + e.stopPropagation(); + e.preventDefault(); + vboxChooser.showOnlyGroupElm($(this).closest('div.vboxChooserGroup')); + return false; + }) + + )) + .append($('<span />').html(gname).addClass('vboxChooserGroupName vboxFitToContainer')) + .append( + $('<div />').addClass('vboxChooserDropTarget vboxChooserDropTargetBottom') + .hover(function(){ + if(vboxChooser._draggingGroup) + $(this).addClass('vboxChooserDropTargetHover' + (first ? 'ignore' : '')); + }, function(){ + $(this).removeClass('vboxChooserDropTargetHover'); + }) + + ) + .hover(function(){ + + if(vboxChooser._compact) return; + + $(this).addClass('vboxHover'); + + // Resize title and add hover class? + if(!$(this).parent().hasClass('vboxChooserGroupRoot')) { + + // Set width of title to -group info span width + var infoWidth = $(this).children('span.vboxChooserGroupInfo').width(); + var pWidth = $(this).width(); + + $(this).children('span.vboxChooserGroupName').css({'max-width':(pWidth-infoWidth-20)+'px'}); + + } + + + },function(){ + + // Resize title and remove hover class + $(this).removeClass('vboxHover'); + + if(!$(this).parent().hasClass('vboxChooserGroupRoot')) + $(this).children('span.vboxChooserGroupName').css({'max-width':''}); + + }).on('mousedown',vboxChooser.selectItem) + + ).addClass((first ? 'vboxChooserGroupRoot vboxChooserGroupRootLevel ' : (collapsed ? 'vboxVMGroupCollapsed ' : '')) + 'vboxChooserGroup') + .data({'vmGroupPath':gpath}) + .draggable({'cursorAt':{left: -10, top: -10},'helper':function(){ + + return $(this).clone().addClass('vboxVMGroupCollapsed vboxVMGroupSelected') + .children('div.vboxChooserGroupHeader').removeClass('vboxHover').children('.vboxChooserGroupNameArrowCollapse') + .hide().closest('div.vboxChooserGroup').css({'width':$(this).width()+'px'}); + + + },'start':function() { + + if(!$('#vboxPane').data('vboxSession').admin) return false; + + if(!vboxChooser._editable) return false; + + vboxChooser._draggingGroup = $(this).data('vmGroupPath'); + $(vboxChooser._anchor).disableSelection(); + + },'stop':function(e) { + vboxChooser.vmGroupDropped(e,$(this)); + + }}).append($('<div />').addClass('vboxChooserGroupVMs')); + + + // Bottom drop target + if(!first) { + gHTML.hover(function(){ + $(this).addClass('vboxGroupHover'); }, function() { + $(this).removeClass('vboxGroupHover'); + }).append( + $('<div />').addClass('vboxChooserDropTarget vboxChooserDropTargetBottom') + .hover(function(){ + if(vboxChooser._draggingGroup) + $(this).addClass('vboxChooserDropTargetHover'); + }, function(){ + $(this).removeClass('vboxChooserDropTargetHover'); + }) + ); + } + + + + // Group context menu + $(gHTML).contextMenu({ + menu: vboxChooser._vmGroupContextMenuObj.menuId(), + menusetup: function(el) { + $(el).children('div.vboxChooserGroupHeader').trigger('click'); + } + },function(act,el,pos,d,e){ + vboxChooser._vmGroupContextMenuObj.menuClickCallback(act, el); + }); + + return gHTML; + + + + }, + + /* + * Return selected VM elements + */ + getSelectedVMElements : function() { + return vboxChooser._anchor.find('table.vboxSelected'); + }, + + /* + * Return selected group elements + */ + getSelectedGroupElements : function() { + return vboxChooser._anchor.find('div.vboxVMGroupSelected'); + }, + + + /* + * Start VM list update + */ + start : function(anchorid) { + + // already running? + if(vboxChooser._running) return; + vboxChooser._running = true; + + // Where are we drawn? + if(anchorid) { + vboxChooser._anchorid = anchorid; + vboxChooser._anchor = $('#'+anchorid); + } + + + // Set group definition key + vboxChooser._groupDefinitionKey = $('#vboxPane').data('vboxConfig')['groupDefinitionKey']; + + // Get collapsed group list + vboxChooser._collapsedGroups = vboxGetLocalDataItem($('#vboxPane').data('vboxConfig').key+'-collapsedGroups', true); + if(!vboxChooser._collapsedGroups) vboxChooser._collapsedGroups = []; + else vboxChooser._collapsedGroups = vboxChooser._collapsedGroups.split(','); + + + // Get groups and machine list. datamediator will start listener + $.when(vboxAjaxRequest('vboxGroupDefinitionsGet')).done(function(g) { + + vboxChooser._groupDefs = g.responseData; + + $.when(vboxVMDataMediator.getVMList()).done(function(d) { + vboxChooser.updateList(d); + }); + }); + + }, + + /* + * Stop VM list updates and clear list + */ + stop : function() { + + if(!vboxChooser._running) return; + vboxChooser._running = false; + + vboxChooser._anchor.html("<div id='vboxChooserSpinner' style='text-align: center'><div><img src='images/spinner.gif' /></div></div>"); + + // reset vars + vboxChooser._versionChecked = false; + vboxChooser._selectedList = []; + vboxChooser.selectedVMs = []; + vboxChooser.selectionMode = vboxSelectionModeNone; + + } + + + +}; + +$(document).ready(function(){ + + // Calculate scrollbar width + vboxChooser._scrollbarWidth = getScrollbarWidth(); + + // "Stop" chooser + $('#vboxPane').on('hostChange',function(){ + + vboxChooser.stop(); + + }).on('hostChanged',function(){ + + + vboxChooser.start(); + + // Refresh menus + }).on('vmGroupDefsSaving vmGroupDefsSaved vmSelectionListChanged', function() { + + if(vboxChooser._vmGroupContextMenuObj) + vboxChooser._vmGroupContextMenuObj.update(vboxChooser); + if(vboxChooser._vmContextMenuObj) + vboxChooser._vmContextMenuObj.update(vboxChooser); + + + // Event list queue + }).on('vboxEvents',function(e, eventList) { + + var redrawVMs = []; + var sortGroups = []; + var groupsChanged = false; + var selectedChanged = false; + var resizeElements = false; + + for(var i = 0; i < eventList.length; i++) { + + switch(eventList[i].eventType) { + + //////////////////////////////// + // + // Machine data changed + // + //////////////////////////////// + case 'OnMachineDataChanged': + + // Shorthand + var vmid = eventList[i].machineId; + var data = vboxVMDataMediator.getVMData(vmid); + + // Update VM in list + if(data) { + + // Enforce VM ownership + if($('#vboxPane').data('vboxConfig').enforceVMOwnership && !$('#vboxPane').data('vboxSession').admin && data.owner != $('#vboxPane').data('vboxSession').user) { + break; + } + + redrawVMs[redrawVMs.length] = vmid; + + // Make sure VM has root group at least + if(data.groups.length == 0) data.groups = ['/']; + + // Remove from groups if they have changed + var currGroups = vboxChooser.getGroupsForVM(vmid); + var groupDiff = $(currGroups).not(data.groups); + groupsChanged = groupDiff.length; + for(var a = 0; a < groupDiff.length; a++) { + + var gElm = vboxChooser.getGroupElement(groupDiff[a], false); + if(!$(gElm)[0]) return; + + selectedChanged = (selectedChanged || $(gElm).children('div.vboxChooserGroupVMs').closest('div.vboxVMGroupSelected').length); + + $(gElm).children('div.vboxChooserGroupVMs') + .children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+data.id).empty().remove(); + + } + + // Add to other groups + var groupDiff = $(data.groups).not(currGroups); + groupsChanged = (groupsChanged || groupDiff.length); + for(var a = 0; a < groupDiff.length; a++) { + + var gElm = vboxChooser.getGroupElement(groupDiff[a]); + + // Skip it if it is already there + if($(gElm).children('div.vboxChooserGroupVMs').children('table.vboxChooserItem-'+vboxChooser._anchorid+'-'+data.id)[0]) + continue; + + $(gElm).children('div.vboxChooserGroupVMs') + .append( + vboxChooser.vmHTML(data) + ); + + selectedChanged = (selectedChanged || $(gElm).children('div.vboxChooserGroupVMs').closest('div.vboxVMGroupSelected').length); + + // Sort the group this machine was added to + sortGroups = sortGroups.concat(data.groups); + + } + + resizeElements = (resizeElements || groupsChanged); + + + } + + break; + + ///////////////////////////////// + // + // Snapshot taken / deleted / restored + // + ///////////////////////////////// + case 'OnSnapshotDeleted': + case 'OnSnapshotTaken': + case 'OnSnapshotRestored': + case 'OnSnapshotChanged': + redrawVMs[redrawVMs.length] = eventList[i].machineId; + break; + + ///////////////////////////////////// + // + // Machine registered or unregistered + // + ////////////////////////////////////// + case 'OnMachineRegistered': + + // Shorthand + var vmid = eventList[i].machineId; + + // Unregistered + if(!eventList[i].registered) { + + var wasSelected = vboxChooser.isVMSelected(vmid); + + $('#'+vboxChooser._anchorid +' table.vboxChooserItem-'+vboxChooser._anchorid+'-'+vmid).remove(); + + groupsChanged = true; + + // See if VM was selected + if(wasSelected) { + + selectedChanged = true; + + vboxChooser._selectedList = vboxChooser._selectedList.filter(function(v){ + return (v.type == 'group' || (v.id != vmid)); + }); + + + } + + resizeElements = true; + + + break; + + } + + // Registered + + // Enforce VM ownership + if($('#vboxPane').data('vboxConfig').enforceVMOwnership && !$('#vboxPane').data('vboxSession').admin && eventList[i].enrichmentData.owner != $('#vboxPane').data('vboxSession').user) { + break; + } + + // Add to list + vboxChooser.updateVMElement(eventList[i].enrichmentData, true); + + resizeElements = true; + break; + + + /////////////////////////////////// + // + // Extra data changed + // + //////////////////////////////////// + case 'OnExtraDataChanged': + + if(!eventList[i].machineId && eventList[i].key.indexOf(vboxChooser._groupDefinitionKey) === 0) { + + var path = eventList[i].key.substring(vboxChooser._groupDefinitionKey.length); + if(!path) path = "/"; + var name = path.substring(path.lastIndexOf('/')+1); + var vboxVMGroups = vboxChooser._groupDefs; + var found = false; + + // No current group definitions? + if(!vboxVMGroups) break; + + // Step through each group, comparing + for(var a = 0; a < vboxVMGroups.length; a++) { + if(vboxVMGroups[a].path == path) { + // Sort this group if it is different + if(vboxVMGroups[a].order != eventList[i].value) + sortGroups[sortGroups.length] = path; + found = true; + vboxVMGroups[a] = {'path':path,'name':name,'order':eventList[i].value}; + break; + } + } + + // Add to group if not found + if(!found) { + vboxVMGroups[vboxVMGroups.length] = {'path':path,'name':name,'order':eventList[i].value}; + sortGroups[sortGroups.length] = path; // sort when added + resizeElements = true; + } + + } else { + + switch(eventList[i].key) { + + // redraw when custom icon changes + case 'phpvb/icon': + redrawVMs[redrawVMs.length] = eventList[i].machineId; + break; + } + } + break; + + //////////////////////////////////////// + // + // Session or state change gets redrawn + // + /////////////////////////////////////// + case 'OnSessionStateChanged': + case 'OnMachineStateChanged': + redrawVMs[redrawVMs.length] = eventList[i].machineId; + break; + + } // </ switch eventType >> + + + } // </ for each event > + + // Now redraw each VM + /////////////////////////// + var redrawn = {}; + var updateMenus = false; + for(var i = 0; i < redrawVMs.length; i++) { + + if(redrawn[redrawVMs[i]]) continue; + redrawn[redrawVMs[i]] = true; + + vboxChooser.updateVMElement(vboxVMDataMediator.getVMData(redrawVMs[i])); + + // Update menus if the VM is selected + updateMenus = (updateMenus || vboxChooser.isVMSelected(redrawVMs[i])); + + } + + // Sort groups + var groupsSorted = {}; + for(var i = 0; i < sortGroups.length; i++) { + if(groupsSorted[sortGroups[i]]) continue; + groupsSorted[sortGroups[i]] = true; + var gElm = $(vboxChooser.getGroupElement(sortGroups[i]),false); + if(gElm[0]) + vboxChooser.sortGroup(gElm); + + } + + // Groups changed + if(groupsChanged || sortGroups.length) { + vboxChooser.composeGroupDef(); + } + + + // update selection list + if(selectedChanged) { + + vboxChooser.selectionListChanged(vboxChooser._selectedList); + + } else if(updateMenus) { + + if(vboxChooser._vmGroupContextMenuObj) + vboxChooser._vmGroupContextMenuObj.update(vboxChooser); + + + if(vboxChooser._vmContextMenuObj) + vboxChooser._vmContextMenuObj.update(vboxChooser); + + } + + if(resizeElements) vboxChooser._resizeElements(true); + + + + }); + + + });
\ No newline at end of file diff --git a/js/datamediator.js b/js/datamediator.js index c701958..2b05393 100644 --- a/js/datamediator.js +++ b/js/datamediator.js @@ -1,465 +1,465 @@ -/**
- * @fileOverview Deferred data loader / cacher singleton. Provides vboxDataMediator
- * @author Ian Moore (imoore76 at yahoo dot com)
- * @version $Id: datamediator.js 591 2015-04-11 22:40:47Z imoore76 $
- * @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com)
- */
-
-/**
- * vboxVMDataMediator
- *
- * @see jQuery.deferred
- * @namespace vboxVMDataMediator
- */
-var vboxVMDataMediator = {
-
- /* Promises for data */
- promises : {
- 'getVMDetails':{},
- 'getVMRuntimeData':{}
- },
-
- /* Holds Basic VM data */
- vmData : null,
-
- /* Holds VM details */
- vmDetailsData : {},
-
- /* Holds VM runtime data */
- vmRuntimeData : {},
-
- /* Expire cached promise / data */
- expireVMDetails: function(vmid) {
- vboxVMDataMediator.promises.getVMDetails[vmid] = null;
- vboxVMDataMediator.vmDetailsData[vmid] = null;
- },
- expireVMRuntimeData: function(vmid) {
- vboxVMDataMediator.promises.getVMRuntimeData[vmid] = null;
- vboxVMDataMediator.vmRuntimeData[vmid] = null;
- },
- expireAll: function() {
- for(var i in vboxVMDataMediator.promises) {
- if(typeof(i) != 'string') continue;
- vboxVMDataMediator.promises[i] = {};
- }
- vboxVMDataMediator.vmData = null;
- vboxVMDataMediator.vmRuntimeData = {};
- vboxVMDataMediator.vmDetailsData = {};
- },
-
- /**
- * Get basic vm data
- *
- * @param vmid {String} ID of VM
- * @returns {Object} vm data
- */
- getVMData: function(vmid) {
-
- // VMList must exist
- if(!vboxVMDataMediator.vmData) {
- return;
- }
-
- return vboxVMDataMediator.vmData[vmid];
-
- },
-
- /**
- * Return list of machines, subscribe to running VM events
- * and start the event listener
- *
- * @returns {Object} promise
- */
- getVMList: function() {
-
- // Return array from existing data
- if(vboxVMDataMediator.vmData) {
- var list = [];
- for(var i in vboxVMDataMediator.vmData) {
- if(typeof i != 'string') continue;
- if(i == 'host') continue;
- list.push(vboxVMDataMediator.vmData[i]);
- }
- return list;
- }
-
-
- var mList = $.Deferred();
- $.when(vboxAjaxRequest('vboxGetMachines')).done(function(d) {
-
- var vmData = {};
- var subscribeList = [];
-
- for(var i = 0; i < d.responseData.length; i++) {
-
- // Enforce VM ownership
- if($('#vboxPane').data('vboxConfig').enforceVMOwnership && !$('#vboxPane').data('vboxSession').admin && d.responseData[i].owner != $('#vboxPane').data('vboxSession').user) {
- continue;
- }
-
- vmData[d.responseData[i].id] = d.responseData[i];
-
- if(vboxVMStates.isRunning({'state':d.responseData[i].state}) || vboxVMStates.isPaused({'state':d.responseData[i].state}))
- subscribeList[subscribeList.length] = d.responseData[i].id;
-
- }
-
- // Start event listener
- $.when(vboxEventListener.start(subscribeList)).done(function(){
- vboxVMDataMediator.vmData = vmData;
- mList.resolve(d.responseData);
-
- }).fail(function() {
- mList.reject();
- });
-
-
- }).fail(function() {
- mList.reject();
- });
-
- return mList.promise();
- },
-
- /**
- * Get VM details data
- *
- * @param vmid {String} ID of VM to get data for
- * @param forceRefresh {Boolean} force refresh of VM data
- * @returns {Object} vm data or promise
- */
- getVMDetails: function(vmid, forceRefresh) {
-
- // Data exists
- if(vboxVMDataMediator.vmDetailsData[vmid] && !forceRefresh) {
- vboxVMDataMediator.promises.getVMDetails[vmid] = null;
- return vboxVMDataMediator.vmDetailsData[vmid];
- }
-
- // Promise does not yet exist?
- if(!vboxVMDataMediator.promises.getVMDetails[vmid]) {
-
- vboxVMDataMediator.promises.getVMDetails[vmid] = $.Deferred();
-
- $.when(vboxAjaxRequest('machineGetDetails',{vm:vmid})).done(function(d){
- vboxVMDataMediator.vmDetailsData[d.responseData.id] = d.responseData;
- vboxVMDataMediator.promises.getVMDetails[vmid].resolve(d.responseData);
- }).fail(function(){
- vboxVMDataMediator.promises.getVMDetails[vmid].reject();
- vboxVMDataMediator.promises.getVMDetails[vmid] = null;
- });
-
- }
- return vboxVMDataMediator.promises.getVMDetails[vmid];
- },
-
- /**
- * Get VM's runtime data
- *
- * @param vmid {String} ID of VM to get data for
- * @returns {Object} VM runtime data or promise
- */
- getVMRuntimeData: function(vmid) {
-
- // Data exists
- if(vboxVMDataMediator.vmRuntimeData[vmid]) {
- vboxVMDataMediator.promises.getVMRuntimeData[vmid] = null;
- return vboxVMDataMediator.vmRuntimeData[vmid];
- }
-
- // Promise does not yet exist?
- if(!vboxVMDataMediator.promises.getVMRuntimeData[vmid]) {
-
- vboxVMDataMediator.promises.getVMRuntimeData[vmid] = $.Deferred();
-
- $.when(vboxAjaxRequest('machineGetRuntimeData',{vm:vmid})).done(function(d){
- vboxVMDataMediator.vmRuntimeData[d.responseData.id] = d.responseData;
- if(vboxVMDataMediator.promises.getVMRuntimeData[vmid])
- vboxVMDataMediator.promises.getVMRuntimeData[vmid].resolve(d.responseData);
- }).fail(function(){
- vboxVMDataMediator.promises.getVMRuntimeData[vmid].reject();
- vboxVMDataMediator.promises.getVMRuntimeData[vmid] = null;
- });
-
- }
- return vboxVMDataMediator.promises.getVMRuntimeData[vmid];
- },
-
- /**
- * Return all data for a VM
- * @param vmid {String} ID of VM to get data for
- * @returns promise
- */
- getVMDataCombined : function(vmid) {
-
- // Special case for 'host'
- if(vmid == 'host') {
- var def = $.Deferred();
- $.when(vboxVMDataMediator.getVMDetails(vmid)).done(function(d){
- def.resolve(d);
- }).fail(function(){
- def.reject();
- });
- return def.promise();
- }
-
- if(!vboxVMDataMediator.vmData[vmid]) return;
-
- var runtime = function() { return {};};
- if(vboxVMStates.isRunning({'state':vboxVMDataMediator.vmData[vmid].state}) || vboxVMStates.isPaused({'state':vboxVMDataMediator.vmData[vmid].state})) {
- runtime = vboxVMDataMediator.getVMRuntimeData(vmid);
- }
-
- var def = $.Deferred();
- $.when(vboxVMDataMediator.getVMDetails(vmid), runtime, vboxVMDataMediator.getVMData(vmid)).done(function(d1,d2,d3){
- def.resolve($.extend(true,{},d1,d2,d3));
- }).fail(function(){
- def.reject();
- });
- return def.promise();
-
- },
-
- /**
- * Get new VM data
- * @param vmid {String} ID of VM to get data for
- * @returns {Object} promise
- */
- refreshVMData : function(vmid) {
-
- // Special case for host
- if(vmid == 'host') {
- $('#vboxPane').trigger('vboxOnMachineDataChanged', [{machineId:'host'}]);
- $('#vboxPane').trigger('vboxEvents', [[{eventType:'OnMachineDataChanged',machineId:'host'}]]);
- return;
- }
-
- if(!vboxVMDataMediator.vmData[vmid]) return;
-
- var def = $.Deferred();
- $.when(vboxAjaxRequest('vboxGetMachines',{'vm':vmid})).done(function(d) {
- vm = d.responseData[0];
- vboxVMDataMediator.vmData[vm.id] = vm;
- def.resolve();
- $('#vboxPane').trigger('vboxOnMachineDataChanged', [{machineId:vm.id,enrichmentData:vm}]);
- $('#vboxPane').trigger('vboxEvents', [[{eventType:'OnMachineDataChanged',machineId:vm.id,enrichmentData:vm}]]);
- }).fail(function(){
- def.reject();
- });
-
- return def.promise();
- }
-
-};
-
-/* Events to bind for vboxVMDataMediator when everything is loaded */
-$(document).ready(function(){
-
- /*
- *
- * VirtualBox events
- *
- */
-
- // Raw event to data handlers
- $('#vboxPane').on('vboxOnMachineDataChanged',function(e, eventData) {
-
- vboxVMDataMediator.expireVMDetails(eventData.machineId);
- vboxVMDataMediator.expireVMRuntimeData(eventData.machineId);
-
- if(vboxVMDataMediator.vmData[eventData.machineId] && eventData.enrichmentData) {
- $.extend(true, vboxVMDataMediator.vmData[eventData.machineId], eventData.enrichmentData);
- // $.extend doesn't seem to handle this for some reason
- vboxVMDataMediator.vmData[eventData.machineId].groups = eventData.enrichmentData.groups;
- }
-
- // Machine state change
- }).on('vboxOnMachineStateChanged', function(e, eventData) {
-
- // Only care about it if its in our list
- if(vboxVMDataMediator.vmData[eventData.machineId]) {
-
- vboxVMDataMediator.vmData[eventData.machineId].state = eventData.state;
- vboxVMDataMediator.vmData[eventData.machineId].lastStateChange = eventData.enrichmentData.lastStateChange;
- vboxVMDataMediator.vmData[eventData.machineId].currentStateModified = eventData.enrichmentData.currentStateModified;
-
- // If it's running, subscribe to its events
- if(vboxVMStates.isRunning({'state':eventData.state}) || vboxVMStates.isPaused({'state':eventData.state})) {
-
- // If we already have runtime data, assume we were already subscribed
- if(!vboxVMDataMediator.vmRuntimeData[eventData.machineId]) {
-
- // Tell event listener to subscribe to this machine's events
- vboxEventListener.subscribeVMEvents(eventData.machineId);
- }
-
- } else {
- vboxVMDataMediator.expireVMRuntimeData(eventData.machineId);
- }
- }
-
- // Session state change
- }).on('vboxOnSessionStateChanged', function(e, eventData) {
-
- if(vboxVMDataMediator.vmData[eventData.machineId])
- vboxVMDataMediator.vmData[eventData.machineId].sessionState = eventData.state;
-
-
- // Snapshot changed
- }).on('vboxOnSnapshotTaken vboxOnSnapshotDeleted vboxOnSnapshotChanged vboxOnSnapshotRestored',function(e,eventData) {
-
- if(vboxVMDataMediator.vmData[eventData.machineId]) {
-
- vboxVMDataMediator.vmData[eventData.machineId].currentSnapshotName = eventData.enrichmentData.currentSnapshotName;
- vboxVMDataMediator.vmData[eventData.machineId].currentStateModified = eventData.enrichmentData.currentStateModified;
-
- // Get media again
- $.when(vboxAjaxRequest('vboxGetMedia')).done(function(d){$('#vboxPane').data('vboxMedia',d.responseData);});
-
- }
- if(vboxVMDataMediator.vmDetailsData[eventData.machineId])
- vboxVMDataMediator.vmDetailsData[eventData.machineId].snapshotCount = eventData.enrichmentData.snapshotCount;
-
- // Expire all data for a VM when machine is unregistered
- }).on('vboxOnMachineRegistered', function(e, eventData) {
-
- if(!eventData.registered) {
- vboxVMDataMediator.expireVMDetails(eventData.machineId);
- vboxVMDataMediator.expireVMRuntimeData(eventData.machineId);
- vboxVMDataMediator.vmData[eventData.machineId] = null;
-
- } else if(eventData.enrichmentData) {
-
- // Enforce VM ownership
- if($('#vboxPane').data('vboxConfig').enforceVMOwnership && !$('#vboxPane').data('vboxSession').admin && eventData.enrichmentData.owner != $('#vboxPane').data('vboxSession').user) {
- return;
- }
-
- vboxVMDataMediator.vmData[eventData.enrichmentData.id] = eventData.enrichmentData;
-
- }
-
- //}).on('vboxOnCPUChanged', function(e, vmid) {
-
- /*
- case 'OnCPUChanged':
- $data['cpu'] = $eventDataObject->cpu;
- $data['add'] = $eventDataObject->add;
- $data['dedupId'] .= '-' . $data['cpu'];
- break;
- */
-
- }).on('vboxOnNetworkAdapterChanged', function(e, eventData) {
-
- if(vboxVMDataMediator.vmRuntimeData[eventData.machineId]) {
- $.extend(vboxVMDataMediator.vmRuntimeData[eventData.machineId].networkAdapters[eventData.networkAdapterSlot], eventData.enrichmentData);
- }
-
-
- /* Storage controller of VM changed */
- //}).on('vboxOnStorageControllerChanged', function() {
- /*
- case 'OnStorageControllerChanged':
- $data['machineId'] = $eventDataObject->machineId;
- $data['dedupId'] .= '-'. $data['machineId'];
- break;
- */
-
- }).on('vboxOnMediumChanged', function(e, eventData) {
-
- /* Medium attachment changed */
- if(vboxVMDataMediator.vmRuntimeData[eventData.machineId]) {
- for(var a = 0; a < vboxVMDataMediator.vmRuntimeData[eventData.machineId].storageControllers.length; a++) {
- if(vboxVMDataMediator.vmRuntimeData[eventData.machineId].storageControllers[a].name == eventData.controller) {
- for(var b = 0; b < vboxVMDataMediator.vmRuntimeData[eventData.machineId].storageControllers[a].mediumAttachments.length; b++) {
- if(vboxVMDataMediator.vmRuntimeData[eventData.machineId].storageControllers[a].mediumAttachments[b].port == eventData.port &&
- vboxVMDataMediator.vmRuntimeData[eventData.machineId].storageControllers[a].mediumAttachments[b].device == eventData.device) {
-
- vboxVMDataMediator.vmRuntimeData[eventData.machineId].storageControllers[a].mediumAttachments[b].medium = (eventData.medium ? {id:eventData.medium} : null);
- break;
- }
- }
- break;
- }
- }
- }
-
- /* Shared folders changed */
- //}).on('vboxOnSharedFolderChanged', function() {
-
- // VRDE runtime info
- }).on('vboxOnVRDEServerChanged', function(e, eventData) {
-
- if(vboxVMDataMediator.vmRuntimeData[eventData.machineId]) {
- $.extend(true,vboxVMDataMediator.vmRuntimeData[eventData.machineId].VRDEServer, eventData.enrichmentData);
- }
-
-
- // This only fires when it is enabled
- }).on('vboxOnVRDEServerInfoChanged', function(e, eventData) {
-
- if(vboxVMDataMediator.vmRuntimeData[eventData.machineId]) {
- vboxVMDataMediator.vmRuntimeData[eventData.machineId].VRDEServerInfo.port = eventData.enrichmentData.port;
- vboxVMDataMediator.vmRuntimeData[eventData.machineId].VRDEServer.enabled = eventData.enrichmentData.enabled;
- }
-
-
- // Execution cap
- }).on('vboxOnCPUExecutionCapChanged', function(e, eventData) {
-
- if(vboxVMDataMediator.vmRuntimeData[eventData.machineId]) {
- vboxVMDataMediator.vmRuntimeData[eventData.machineId].CPUExecutionCap = eventData.executionCap;
- }
-
- // Special cases for where phpvirtualbox keeps its extra data
- }).on('vboxOnExtraDataChanged', function(e, eventData) {
-
- // No vm id is a global change
- if(!eventData.machineId || !vboxVMDataMediator.vmData[eventData.machineId]) return;
-
- switch(eventData.key) {
-
- // Startup mode
- case 'pvbx/startupMode':
- if(vboxVMDataMediator.vmDetailsData[eventData.machineId])
- vboxVMDataMediator.vmDetailsData[eventData.machineId].startupMode = eventData.value;
- break;
-
- // Owner
- case 'phpvb/sso/owner':
- vboxVMDataMediator.vmData[eventData.machineId].owner = eventData.value;
- break;
-
- // Custom icon
- case 'phpvb/icon':
-
- vboxVMDataMediator.vmData[eventData.machineId].customIcon = eventData.value;
-
- if(vboxVMDataMediator.vmDetailsData[eventData.machineId])
- vboxVMDataMediator.vmDetailsData[eventData.machineId].customIcon = eventData.value;
-
-
- break;
-
- // First time run
- case 'GUI/FirstRun':
- if(vboxVMDataMediator.vmDetailsData[eventData.machineId])
- vboxVMDataMediator.vmDetailsData[eventData.machineId].GUI.FirstRun = eventData.value;
- break;
-
- }
-
-
- /*
- *
- * phpVirtualBox events
- *
- */
-
- // Expire everything when host changes
- }).on('hostChange',function(){
- vboxVMDataMediator.expireAll();
-
- });
-
+/** + * @fileOverview Deferred data loader / cacher singleton. Provides vboxDataMediator + * @author Ian Moore (imoore76 at yahoo dot com) + * @version $Id: datamediator.js 591 2015-04-11 22:40:47Z imoore76 $ + * @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com) + */ + +/** + * vboxVMDataMediator + * + * @see jQuery.deferred + * @namespace vboxVMDataMediator + */ +var vboxVMDataMediator = { + + /* Promises for data */ + promises : { + 'getVMDetails':{}, + 'getVMRuntimeData':{} + }, + + /* Holds Basic VM data */ + vmData : null, + + /* Holds VM details */ + vmDetailsData : {}, + + /* Holds VM runtime data */ + vmRuntimeData : {}, + + /* Expire cached promise / data */ + expireVMDetails: function(vmid) { + vboxVMDataMediator.promises.getVMDetails[vmid] = null; + vboxVMDataMediator.vmDetailsData[vmid] = null; + }, + expireVMRuntimeData: function(vmid) { + vboxVMDataMediator.promises.getVMRuntimeData[vmid] = null; + vboxVMDataMediator.vmRuntimeData[vmid] = null; + }, + expireAll: function() { + for(var i in vboxVMDataMediator.promises) { + if(typeof(i) != 'string') continue; + vboxVMDataMediator.promises[i] = {}; + } + vboxVMDataMediator.vmData = null; + vboxVMDataMediator.vmRuntimeData = {}; + vboxVMDataMediator.vmDetailsData = {}; + }, + + /** + * Get basic vm data + * + * @param vmid {String} ID of VM + * @returns {Object} vm data + */ + getVMData: function(vmid) { + + // VMList must exist + if(!vboxVMDataMediator.vmData) { + return; + } + + return vboxVMDataMediator.vmData[vmid]; + + }, + + /** + * Return list of machines, subscribe to running VM events + * and start the event listener + * + * @returns {Object} promise + */ + getVMList: function() { + + // Return array from existing data + if(vboxVMDataMediator.vmData) { + var list = []; + for(var i in vboxVMDataMediator.vmData) { + if(typeof i != 'string') continue; + if(i == 'host') continue; + list.push(vboxVMDataMediator.vmData[i]); + } + return list; + } + + + var mList = $.Deferred(); + $.when(vboxAjaxRequest('vboxGetMachines')).done(function(d) { + + var vmData = {}; + var subscribeList = []; + + for(var i = 0; i < d.responseData.length; i++) { + + // Enforce VM ownership + if($('#vboxPane').data('vboxConfig').enforceVMOwnership && !$('#vboxPane').data('vboxSession').admin && d.responseData[i].owner != $('#vboxPane').data('vboxSession').user) { + continue; + } + + vmData[d.responseData[i].id] = d.responseData[i]; + + if(vboxVMStates.isRunning({'state':d.responseData[i].state}) || vboxVMStates.isPaused({'state':d.responseData[i].state})) + subscribeList[subscribeList.length] = d.responseData[i].id; + + } + + // Start event listener + $.when(vboxEventListener.start(subscribeList)).done(function(){ + vboxVMDataMediator.vmData = vmData; + mList.resolve(d.responseData); + + }).fail(function() { + mList.reject(); + }); + + + }).fail(function() { + mList.reject(); + }); + + return mList.promise(); + }, + + /** + * Get VM details data + * + * @param vmid {String} ID of VM to get data for + * @param forceRefresh {Boolean} force refresh of VM data + * @returns {Object} vm data or promise + */ + getVMDetails: function(vmid, forceRefresh) { + + // Data exists + if(vboxVMDataMediator.vmDetailsData[vmid] && !forceRefresh) { + vboxVMDataMediator.promises.getVMDetails[vmid] = null; + return vboxVMDataMediator.vmDetailsData[vmid]; + } + + // Promise does not yet exist? + if(!vboxVMDataMediator.promises.getVMDetails[vmid]) { + + vboxVMDataMediator.promises.getVMDetails[vmid] = $.Deferred(); + + $.when(vboxAjaxRequest('machineGetDetails',{vm:vmid})).done(function(d){ + vboxVMDataMediator.vmDetailsData[d.responseData.id] = d.responseData; + vboxVMDataMediator.promises.getVMDetails[vmid].resolve(d.responseData); + }).fail(function(){ + vboxVMDataMediator.promises.getVMDetails[vmid].reject(); + vboxVMDataMediator.promises.getVMDetails[vmid] = null; + }); + + } + return vboxVMDataMediator.promises.getVMDetails[vmid]; + }, + + /** + * Get VM's runtime data + * + * @param vmid {String} ID of VM to get data for + * @returns {Object} VM runtime data or promise + */ + getVMRuntimeData: function(vmid) { + + // Data exists + if(vboxVMDataMediator.vmRuntimeData[vmid]) { + vboxVMDataMediator.promises.getVMRuntimeData[vmid] = null; + return vboxVMDataMediator.vmRuntimeData[vmid]; + } + + // Promise does not yet exist? + if(!vboxVMDataMediator.promises.getVMRuntimeData[vmid]) { + + vboxVMDataMediator.promises.getVMRuntimeData[vmid] = $.Deferred(); + + $.when(vboxAjaxRequest('machineGetRuntimeData',{vm:vmid})).done(function(d){ + vboxVMDataMediator.vmRuntimeData[d.responseData.id] = d.responseData; + if(vboxVMDataMediator.promises.getVMRuntimeData[vmid]) + vboxVMDataMediator.promises.getVMRuntimeData[vmid].resolve(d.responseData); + }).fail(function(){ + vboxVMDataMediator.promises.getVMRuntimeData[vmid].reject(); + vboxVMDataMediator.promises.getVMRuntimeData[vmid] = null; + }); + + } + return vboxVMDataMediator.promises.getVMRuntimeData[vmid]; + }, + + /** + * Return all data for a VM + * @param vmid {String} ID of VM to get data for + * @returns promise + */ + getVMDataCombined : function(vmid) { + + // Special case for 'host' + if(vmid == 'host') { + var def = $.Deferred(); + $.when(vboxVMDataMediator.getVMDetails(vmid)).done(function(d){ + def.resolve(d); + }).fail(function(){ + def.reject(); + }); + return def.promise(); + } + + if(!vboxVMDataMediator.vmData[vmid]) return; + + var runtime = function() { return {};}; + if(vboxVMStates.isRunning({'state':vboxVMDataMediator.vmData[vmid].state}) || vboxVMStates.isPaused({'state':vboxVMDataMediator.vmData[vmid].state})) { + runtime = vboxVMDataMediator.getVMRuntimeData(vmid); + } + + var def = $.Deferred(); + $.when(vboxVMDataMediator.getVMDetails(vmid), runtime, vboxVMDataMediator.getVMData(vmid)).done(function(d1,d2,d3){ + def.resolve($.extend(true,{},d1,d2,d3)); + }).fail(function(){ + def.reject(); + }); + return def.promise(); + + }, + + /** + * Get new VM data + * @param vmid {String} ID of VM to get data for + * @returns {Object} promise + */ + refreshVMData : function(vmid) { + + // Special case for host + if(vmid == 'host') { + $('#vboxPane').trigger('vboxOnMachineDataChanged', [{machineId:'host'}]); + $('#vboxPane').trigger('vboxEvents', [[{eventType:'OnMachineDataChanged',machineId:'host'}]]); + return; + } + + if(!vboxVMDataMediator.vmData[vmid]) return; + + var def = $.Deferred(); + $.when(vboxAjaxRequest('vboxGetMachines',{'vm':vmid})).done(function(d) { + vm = d.responseData[0]; + vboxVMDataMediator.vmData[vm.id] = vm; + def.resolve(); + $('#vboxPane').trigger('vboxOnMachineDataChanged', [{machineId:vm.id,enrichmentData:vm}]); + $('#vboxPane').trigger('vboxEvents', [[{eventType:'OnMachineDataChanged',machineId:vm.id,enrichmentData:vm}]]); + }).fail(function(){ + def.reject(); + }); + + return def.promise(); + } + +}; + +/* Events to bind for vboxVMDataMediator when everything is loaded */ +$(document).ready(function(){ + + /* + * + * VirtualBox events + * + */ + + // Raw event to data handlers + $('#vboxPane').on('vboxOnMachineDataChanged',function(e, eventData) { + + vboxVMDataMediator.expireVMDetails(eventData.machineId); + vboxVMDataMediator.expireVMRuntimeData(eventData.machineId); + + if(vboxVMDataMediator.vmData[eventData.machineId] && eventData.enrichmentData) { + $.extend(true, vboxVMDataMediator.vmData[eventData.machineId], eventData.enrichmentData); + // $.extend doesn't seem to handle this for some reason + vboxVMDataMediator.vmData[eventData.machineId].groups = eventData.enrichmentData.groups; + } + + // Machine state change + }).on('vboxOnMachineStateChanged', function(e, eventData) { + + // Only care about it if its in our list + if(vboxVMDataMediator.vmData[eventData.machineId]) { + + vboxVMDataMediator.vmData[eventData.machineId].state = eventData.state; + vboxVMDataMediator.vmData[eventData.machineId].lastStateChange = eventData.enrichmentData.lastStateChange; + vboxVMDataMediator.vmData[eventData.machineId].currentStateModified = eventData.enrichmentData.currentStateModified; + + // If it's running, subscribe to its events + if(vboxVMStates.isRunning({'state':eventData.state}) || vboxVMStates.isPaused({'state':eventData.state})) { + + // If we already have runtime data, assume we were already subscribed + if(!vboxVMDataMediator.vmRuntimeData[eventData.machineId]) { + + // Tell event listener to subscribe to this machine's events + vboxEventListener.subscribeVMEvents(eventData.machineId); + } + + } else { + vboxVMDataMediator.expireVMRuntimeData(eventData.machineId); + } + } + + // Session state change + }).on('vboxOnSessionStateChanged', function(e, eventData) { + + if(vboxVMDataMediator.vmData[eventData.machineId]) + vboxVMDataMediator.vmData[eventData.machineId].sessionState = eventData.state; + + + // Snapshot changed + }).on('vboxOnSnapshotTaken vboxOnSnapshotDeleted vboxOnSnapshotChanged vboxOnSnapshotRestored',function(e,eventData) { + + if(vboxVMDataMediator.vmData[eventData.machineId]) { + + vboxVMDataMediator.vmData[eventData.machineId].currentSnapshotName = eventData.enrichmentData.currentSnapshotName; + vboxVMDataMediator.vmData[eventData.machineId].currentStateModified = eventData.enrichmentData.currentStateModified; + + // Get media again + $.when(vboxAjaxRequest('vboxGetMedia')).done(function(d){$('#vboxPane').data('vboxMedia',d.responseData);}); + + } + if(vboxVMDataMediator.vmDetailsData[eventData.machineId]) + vboxVMDataMediator.vmDetailsData[eventData.machineId].snapshotCount = eventData.enrichmentData.snapshotCount; + + // Expire all data for a VM when machine is unregistered + }).on('vboxOnMachineRegistered', function(e, eventData) { + + if(!eventData.registered) { + vboxVMDataMediator.expireVMDetails(eventData.machineId); + vboxVMDataMediator.expireVMRuntimeData(eventData.machineId); + vboxVMDataMediator.vmData[eventData.machineId] = null; + + } else if(eventData.enrichmentData) { + + // Enforce VM ownership + if($('#vboxPane').data('vboxConfig').enforceVMOwnership && !$('#vboxPane').data('vboxSession').admin && eventData.enrichmentData.owner != $('#vboxPane').data('vboxSession').user) { + return; + } + + vboxVMDataMediator.vmData[eventData.enrichmentData.id] = eventData.enrichmentData; + + } + + //}).on('vboxOnCPUChanged', function(e, vmid) { + + /* + case 'OnCPUChanged': + $data['cpu'] = $eventDataObject->cpu; + $data['add'] = $eventDataObject->add; + $data['dedupId'] .= '-' . $data['cpu']; + break; + */ + + }).on('vboxOnNetworkAdapterChanged', function(e, eventData) { + + if(vboxVMDataMediator.vmRuntimeData[eventData.machineId]) { + $.extend(vboxVMDataMediator.vmRuntimeData[eventData.machineId].networkAdapters[eventData.networkAdapterSlot], eventData.enrichmentData); + } + + + /* Storage controller of VM changed */ + //}).on('vboxOnStorageControllerChanged', function() { + /* + case 'OnStorageControllerChanged': + $data['machineId'] = $eventDataObject->machineId; + $data['dedupId'] .= '-'. $data['machineId']; + break; + */ + + }).on('vboxOnMediumChanged', function(e, eventData) { + + /* Medium attachment changed */ + if(vboxVMDataMediator.vmRuntimeData[eventData.machineId]) { + for(var a = 0; a < vboxVMDataMediator.vmRuntimeData[eventData.machineId].storageControllers.length; a++) { + if(vboxVMDataMediator.vmRuntimeData[eventData.machineId].storageControllers[a].name == eventData.controller) { + for(var b = 0; b < vboxVMDataMediator.vmRuntimeData[eventData.machineId].storageControllers[a].mediumAttachments.length; b++) { + if(vboxVMDataMediator.vmRuntimeData[eventData.machineId].storageControllers[a].mediumAttachments[b].port == eventData.port && + vboxVMDataMediator.vmRuntimeData[eventData.machineId].storageControllers[a].mediumAttachments[b].device == eventData.device) { + + vboxVMDataMediator.vmRuntimeData[eventData.machineId].storageControllers[a].mediumAttachments[b].medium = (eventData.medium ? {id:eventData.medium} : null); + break; + } + } + break; + } + } + } + + /* Shared folders changed */ + //}).on('vboxOnSharedFolderChanged', function() { + + // VRDE runtime info + }).on('vboxOnVRDEServerChanged', function(e, eventData) { + + if(vboxVMDataMediator.vmRuntimeData[eventData.machineId]) { + $.extend(true,vboxVMDataMediator.vmRuntimeData[eventData.machineId].VRDEServer, eventData.enrichmentData); + } + + + // This only fires when it is enabled + }).on('vboxOnVRDEServerInfoChanged', function(e, eventData) { + + if(vboxVMDataMediator.vmRuntimeData[eventData.machineId]) { + vboxVMDataMediator.vmRuntimeData[eventData.machineId].VRDEServerInfo.port = eventData.enrichmentData.port; + vboxVMDataMediator.vmRuntimeData[eventData.machineId].VRDEServer.enabled = eventData.enrichmentData.enabled; + } + + + // Execution cap + }).on('vboxOnCPUExecutionCapChanged', function(e, eventData) { + + if(vboxVMDataMediator.vmRuntimeData[eventData.machineId]) { + vboxVMDataMediator.vmRuntimeData[eventData.machineId].CPUExecutionCap = eventData.executionCap; + } + + // Special cases for where phpvirtualbox keeps its extra data + }).on('vboxOnExtraDataChanged', function(e, eventData) { + + // No vm id is a global change + if(!eventData.machineId || !vboxVMDataMediator.vmData[eventData.machineId]) return; + + switch(eventData.key) { + + // Startup mode + case 'pvbx/startupMode': + if(vboxVMDataMediator.vmDetailsData[eventData.machineId]) + vboxVMDataMediator.vmDetailsData[eventData.machineId].startupMode = eventData.value; + break; + + // Owner + case 'phpvb/sso/owner': + vboxVMDataMediator.vmData[eventData.machineId].owner = eventData.value; + break; + + // Custom icon + case 'phpvb/icon': + + vboxVMDataMediator.vmData[eventData.machineId].customIcon = eventData.value; + + if(vboxVMDataMediator.vmDetailsData[eventData.machineId]) + vboxVMDataMediator.vmDetailsData[eventData.machineId].customIcon = eventData.value; + + + break; + + // First time run + case 'GUI/FirstRun': + if(vboxVMDataMediator.vmDetailsData[eventData.machineId]) + vboxVMDataMediator.vmDetailsData[eventData.machineId].GUI.FirstRun = eventData.value; + break; + + } + + + /* + * + * phpVirtualBox events + * + */ + + // Expire everything when host changes + }).on('hostChange',function(){ + vboxVMDataMediator.expireAll(); + + }); + });
\ No newline at end of file diff --git a/js/dialogs.js b/js/dialogs.js index af72d51..402ab55 100644 --- a/js/dialogs.js +++ b/js/dialogs.js @@ -1,1725 +1,1725 @@ -/**
- * @fileOverview Dialog logic for various wizards and other dialogs
- * @author Ian Moore (imoore76 at yahoo dot com)
- * @version $Id: dialogs.js 599 2015-07-27 10:40:37Z imoore76 $
- * @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com)
- */
-
-/**
- * Run the import appliance wizard
- */
-function vboxWizardImportApplianceDialog() {
-
- // reference
- var self = this;
-
- // Extend vboxWizard
- this.parentClass = vboxWizard;
- this.parentClass();
-
- /* Common settings */
- this.name = 'wizardImportAppliance';
- this.title = trans('Import Virtual Appliance','UIWizardImportApp');
- this.bg = 'images/vbox/vmw_ovf_import_bg.png';
- this.icon = 'import';
- this.steps = 2;
- this.height = 500;
- this.finishText = trans('Import','UIWizardImportApp');
- this.context = 'UIWizardImportApp';
-
- /* Restore defaults is added to last step */
- this.stepButtons = [
- {
- 'name' : trans('Restore Defaults','UIWizardImportApp'),
- 'steps' : [-1],
- 'click' : function() {
- wizardImportApplianceParsed();
- }
- }
- ];
-
- /* Data to be loaded */
- this.data = [
- {'fn':'vboxGetEnumerationMap','args':{'class':'NetworkAdapterType'},'callback':function(d){$('#vboxPane').data('vboxNetworkAdapterTypes',d.responseData);}},
- {'fn':'vboxGetEnumerationMap','args':{'class':'AudioControllerType'},'callback':function(d){$('#vboxPane').data('vboxAudioControllerTypes',d.responseData);}},
- ];
-
- /* Perform action on finish */
- this.onFinish = function() {
-
- var file = $(self.form).find('[name=wizardImportApplianceLocation]').val();
- var descriptions = $('#vboxImportProps').data('descriptions');
- var reinitNetwork = $(self.form).find('[name=vboxImportReinitNetwork]').prop('checked');
-
- // Check for descriptions
- if(!descriptions) {
- return;
- }
-
-
- /** Call Appliance import AJAX function */
- var vboxImportApp = function() {
-
- $(self.dialog).empty().remove();
-
- var l = new vboxLoader();
- l.add('applianceImport',function(d){
- if(d.responseData.progress) {
- vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(){
- // Imported media must be refreshed
- var ml = new vboxLoader();
- ml.add('vboxGetMedia',function(d){$('#vboxPane').data('vboxMedia',d.responseData);});
- ml.onLoad = function(){
- self.completed.resolve();
- };
- ml.run();
- },'progress_import_90px.png',trans('Import an appliance into VirtualBox','UIActionPool').replace(/\.+$/g,''),vboxBasename(file));
- } else {
- self.completed.reject();
- }
- },{'descriptions':descriptions,'file':file,'reinitNetwork':reinitNetwork});
- l.run();
- };
-
- // license agreements
- var licenses = [];
-
- // Step through each VM and obtain value
- for(var a = 0; a < descriptions.length; a++) {
- var children = $('#vboxImportProps').children('tr.vboxChildOf'+a);
- descriptions[a][5] = []; // enabled / disabled
- var lic = null;
- var vmname = null;
- for(var b = 0; b < children.length; b++) {
- descriptions[a][5][b] = !$(children[b]).data('propdisabled');
- descriptions[a][3][$(children[b]).data('descOrder')] = $(children[b]).children('td:eq(1)').data('descValue');
- // check for license
- if(descriptions[a][0][b] == 'License') {
- lic = descriptions[a][2][b];
- } else if(descriptions[a][0][b] == 'Name') {
- vmname = descriptions[a][2][b];
- }
- }
- if(lic) {
- if(!vmname) vmname = trans('Virtual System %1','UIApplianceEditorWidget').replace('%1',a);
- licenses[licenses.length] = {'name':vmname,'license':lic};
- }
- }
-
-
- if(licenses.length) {
-
- var msg = trans('<b>The virtual system "%1" requires that you agree to the terms and conditions of the software license agreement shown below.</b><br /><br />Click <b>Agree</b> to continue or click <b>Disagree</b> to cancel the import.','UIImportLicenseViewer');
- var a = 0;
- var buttons = {};
- buttons[trans('Agree','UIImportLicenseViewer')] = function() {
-
- a++;
- if(a >= licenses.length) {
- $(this).remove();
- vboxImportApp();
- return;
- }
- $(this).dialog('close');
- $('#vboxImportWizLicTitle').html(msg.replace('%1',licenses[a]['name']));
- $('#vboxImportWizLicContent').val(licenses[a]['license']);
- $(this).dialog('open');
-
- };
- buttons[trans('Disagree','UIImportLicenseViewer')] = function() {
- $(this).empty().remove();
- };
-
- var dlg = $('<div />').dialog({'closeOnEscape':false,'width':600,'height':500,'buttons':buttons,'modal':true,'autoOpen':false,'dialogClass':'vboxDialogContent vboxWizard','title':'<img src="images/vbox/os_type_16px.png" class="vboxDialogTitleIcon" /> ' + trans('Software License Agreement','UIImportLicenseViewer')});
-
- $(dlg).html('<p id="vboxImportWizLicTitle" /><textarea rows="20" spellcheck="false" wrap="off" readonly="true"id="vboxImportWizLicContent" style="width:100%; margin:2px; padding:2px;"></textarea>');
- $('#vboxImportWizLicTitle').html(msg.replace('%1',licenses[a]['name']));
- $('#vboxImportWizLicContent').val(licenses[a]['license']);
- $(dlg).dialog('open');
-
- } else {
- vboxImportApp();
- }
-
-
- };
-
-}
-
-/**
- * Run the export appliance wizard
- */
-function vboxWizardExportApplianceDialog() {
-
- // reference
- var self = this;
-
- // Extend vboxWizard class
- this.parentClass = vboxWizard;
- this.parentClass();
-
- /* Common settings */
- this.name = 'wizardExportAppliance';
- this.title = trans('Export Virtual Appliance','UIWizardExportApp');
- this.bg = 'images/vbox/vmw_ovf_export_bg.png';
- this.icon = 'export';
- this.steps = 4;
- this.height = 500;
- this.context = 'UIWizardExportApp';
- this.finishText = trans('Export','UIWizardExportApp');
-
- /* Add restore defaults button to last step */
- this.stepButtons = [
- {
- 'name' : trans('Restore Defaults','UIWizardExportApp'),
- 'steps' : [-1],
- 'click' : function(e) {
- // Remove export VM properties
- $('#vboxExportProps').children().remove();
- // Re-trigger adding them
- if(self.mode == 'advanced')
- vboxWizardExportApplianceUpdateList();
- else
- $('#wizardExportApplianceStep4').trigger('show',e,self);
- }
- }
- ];
-
- /* Function run when wizard completes */
- this.onFinish = function() {
-
-
- // Actually export appliances
- function vboxExportApp(force) {
-
- // Each VM
- var vmid = null;
- var vms = {};
- var vmsAndProps = $('#vboxExportProps').children('tr');
- for(var a = 0; a < vmsAndProps.length; a++) {
- if($(vmsAndProps[a]).hasClass('vboxTableParent')) {
- vmid = $(vmsAndProps[a]).data('vm').id;
- vms[vmid] = {};
- vms[vmid]['id'] = vmid;
- continue;
- }
-
- var prop = $(vmsAndProps[a]).data('vmprop');
- vms[vmid][prop] = $(vmsAndProps[a]).children('td:eq(1)').children().first().text();
-
- }
-
- var file = $(self.form).find('[name=wizardExportApplianceLocation]').val();
- var format = $(self.form).find('[name=wizardExportApplianceFormat]').val();
- var manifest = $(self.form).find('[name=wizardExportApplianceManifest]').prop('checked');
- var overwrite = force;
-
- var l = new vboxLoader();
- l.add('applianceExport',function(d){
- if(d.responseData.progress) {
- vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(){
- self.completed.resolve();
- },'progress_export_90px.png',
- trans('Export one or more VirtualBox virtual machines as an appliance','UIActionPool').replace(/\.+$/g,''),
- vboxBasename(file));
- } else {
- self.completed.reject();
- }
- },{'format':format,'file':file,'vms':vms,'manifest':manifest,'overwrite':overwrite});
- l.run();
-
- $(self.dialog).empty().remove();
-
-
- }
-
- /* Remove required classes */
- $(self.form).find('[name=wizardExportApplianceLocation]').removeClass('vboxRequired');
- $('#vboxExportAppVMListContainer').removeClass('vboxRequired');
-
- /* Some vms must be selected */
- if($('#vboxExportProps').children('tr').length == 0) {
- $('#vboxExportAppVMListContainer').addClass('vboxRequired');
- return;
- }
-
- /* Check to see if file exists */
- var loc = $(self.form).find('[name=wizardExportApplianceLocation]').val();
- if(!loc) {
- $(self.form).find('[name=wizardExportApplianceLocation]').addClass('vboxRequired');
- return;
- }
-
- var fileExists = false;
- var fe = new vboxLoader();
- fe.add('fileExists',function(d){
- fileExists = d.responseData;
- },{'file':loc});
- fe.onLoad = function() {
- if(fileExists) {
- var buttons = {};
- buttons[trans('Yes','QIMessageBox')] = function() {
- vboxExportApp(1);
- $(this).empty().remove();
- };
- vboxConfirm(trans('A file named <b>%1</b> already exists. Are you sure you want to replace it?<br /><br />Replacing it will overwrite its contents.','UIMessageCenter').replace('%1',vboxBasename(loc)),buttons,trans('No','QIMessageBox'));
- return;
- }
- vboxExportApp(0);
-
- };
- fe.run();
-
- };
-
-}
-
-/**
- * Show the medium encryption dialog
- *
- * @param {String} context - used in dialog name
- * @param {Array} encIds - encryption ids
- *
- */
-function vboxMediumEncryptionPasswordsDialog(context, encIds, validIds) {
-
- if(!(encIds && encIds.length)) { return []; }
-
- var results = $.Deferred();
-
- var dialogTitle = trans("%1 - Disk Encryption").replace('%1', context);
-
- var l = new vboxLoader();
- l.addFileToDOM("panes/mediumEncryptionPasswords.html");
- l.onLoad = function() {
-
- for(var i = 0; i < encIds.length; i++) {
- vboxMediumEncryptionPasswordAdd(encIds[i].id, validIds && validIds.length && jQuery.inArray(encIds[i].id, validIds) > -1);
- }
-
- var buttons = {};
- buttons[trans('OK','QIMessageBox')] = function(){
- // Get passwords
- var pws = vboxMediumEncryptionPasswordsGet();
- if(pws === false)
- return;
-
- $(this).trigger('close').empty().remove();
-
- results.resolve(pws);
- };
- buttons[trans('Cancel','QIMessageBox')] = function(){
- results.reject();
- $(this).trigger('close').empty().remove();
- };
-
- $('#vboxMediumEncryptionPasswords').dialog({'closeOnEscape':true,'width':600,'height':400,'buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent','title':'<img src="images/vbox/nw_16px.png" class="vboxDialogTitleIcon" /> ' + dialogTitle}).on("dialogbeforeclose",function(){
- $(this).parent().find('span:contains("'+trans('Cancel','QIMessageBox')+'")').trigger('click');
- });
-
- };
-
- l.run()
-
- return results.promise();
-}
-
-/**
- * Show the port forwarding configuration dialog
- * @param {Array} rules - list of port forwarding rules to process
- * @returns {Object} deferred promise
- */
-function vboxPortForwardConfigDialog(rules) {
-
- var results = $.Deferred();
-
- var l = new vboxLoader();
- l.addFileToDOM("panes/settingsPortForwarding.html");
- l.onLoad = function(){
-
- vboxSettingsPortForwardingInit(rules);
-
- var resizeTable = function() {
- var h = $('#vboxSettingsPortForwarding').children('table').hide().parent().innerHeight() - 16;
- $('#vboxSettingsPortForwarding').children('table').css({'height':h+'px'}).show();
- $('#vboxSettingsPortForwardingListDiv').css({'height':(h-6)+'px','overflow':'auto'});
-
-
- };
-
- var buttons = {};
- buttons[trans('OK','QIMessageBox')] = function(){
- // Get rules
- var rules = $('#vboxSettingsPortForwardingList').children('tr');
- var rulesToPass = new Array();
- for(var i = 0; i < rules.length; i++) {
- if($(rules[i]).data('vboxRule')[3] == 0 || $(rules[i]).data('vboxRule')[5] == 0) {
- vboxAlert(trans("The current port forwarding rules are not valid. " +
- "None of the host or guest port values may be set to zero.",'UIMessageCenter'));
- return;
- }
- rulesToPass[i] = $(rules[i]).data('vboxRule');
- }
- $(this).trigger('close').empty().remove();
-
- results.resolve(rulesToPass);
- };
- buttons[trans('Cancel','QIMessageBox')] = function(){
- results.reject();
- $(this).trigger('close').empty().remove();
- };
-
- $('#vboxSettingsPortForwarding').dialog({'closeOnEscape':true,'width':600,'height':400,'buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent','title':'<img src="images/vbox/nw_16px.png" class="vboxDialogTitleIcon" /> ' + trans('Port Forwarding Rules','UIMachineSettingsPortForwardingDlg')}).on("dialogbeforeclose",function(){
- $(this).parent().find('span:contains("'+trans('Cancel','QIMessageBox')+'")').trigger('click');
- }).on('dialogresizestop',resizeTable);
-
- resizeTable();
- };
- l.run();
-
- return results.promise();
-}
-
-/**
- * Run the New Virtual Machine Wizard
- * @param {String} vmgroup - VM group to add machine to
- */
-function vboxWizardNewVMDialog(vmgroup) {
-
- // reference to self
- var self = this;
-
- // Extend vboxWizard class
- this.parentClass = vboxWizard;
- this.parentClass();
-
- /* Common settings */
- this.name = 'wizardNewVM';
- this.bg = 'images/vbox/vmw_new_welcome_bg.png';
- this.title = trans('Create Virtual Machine','UIWizardNewVM');
- this.icon = 'vm_new';
- this.steps = 3;
- this.vmgroup = vmgroup;
- this.context = 'UIWizardNewVM';
- this.finishText = trans('Create','UIWizardNewVM');
- this.data = [
- {'fn':'vboxGetMedia','callback':function(d){$('#vboxPane').data('vboxMedia',d.responseData);}}
- ];
-
- /* Function to run when wizard completes */
- this.onFinish = function() {
-
- // Callback to finish wizard
- var vboxNewVMFinish = function() {
-
- // Get parameters
- var disk = '';
- if($(self.form).find('[name=newVMDisk]:checked').val() == 'existing') {
- disk = $(self.form)[0].newVMDiskSelect.options[$(self.form)[0].newVMDiskSelect.selectedIndex].value;
- disk = vboxMedia.getMediumById(disk).location;
- }
- var name = jQuery.trim($(self.form).find('[name=newVMName]').val());
- var ostype = $(self.form).find('[name=newVMOSType]').val();
- var mem = parseInt($(self.form).find('[name=wizardNewVMSizeValue]').val());
-
- // Show loading screen
- var l = new vboxLoader('machineCreate');
- l.showLoading();
-
- $.when(vboxAjaxRequest('machineCreate',{'disk':disk,'ostype':ostype,'memory':mem,'name':name,'group':vmgroup}))
- .always(function() {
- l.removeLoading();
- }).done(function(res){
-
- if(res.responseData.exists) {
- vboxAlert(trans('<p>Cannot create the machine folder <b>%1</b> in the parent folder <nobr><b>%2</b>.</nobr></p><p>This folder already exists and possibly belongs to another machine.</p>','UIMessageCenter').replace('%1',vboxBasename(res.exists)).replace('%2',vboxDirname(res.exists)));
-
- } else if(res.success) {
- $(self.dialog).empty().remove();
- var lm = new vboxLoader('machineCreate');
- lm.add('vboxGetMedia',function(d){$('#vboxPane').data('vboxMedia',d.responseData);});
- lm.onLoad = function() {
- self.completed.resolve();
- };
- lm.run();
- } else {
- self.completed.reject();
- }
-
- });
-
- };
-
- // Name must exist
- if(!jQuery.trim($(self.form).find('[name=newVMName]').val())) {
- $(self.form).find('[name=newVMName]').addClass('vboxRequired');
- return;
- }
- $(self.form).find('[name=newVMName]').removeClass('vboxRequired');
-
- // Make sure file does not exist
- var fnl = new vboxLoader();
- fnl.add('vboxGetComposedMachineFilename',function(d){
-
- loc = vboxDirname(d.responseData);
- var fileExists = false;
-
- var fe = new vboxLoader('fileExists');
- fe.add('fileExists',function(d){
- fileExists = d.responseData;
- },{'file':loc});
- fe.onLoad = function() {
-
- if(fileExists) {
-
- vboxAlert(trans('<p>Cannot create the machine folder <b>%1</b> in the parent folder <nobr><b>%2</b>.</nobr></p><p>This folder already exists and possibly belongs to another machine.</p>','UIMessageCenter').replace('%1',vboxBasename(loc)).replace('%2',vboxDirname(loc)));
- // Go back
- self.displayStep(1);
-
- return;
- }
-
- // Start new harddisk wizard if create new is selected
- if($(self.form).find('[name=newVMDisk]:checked').val() == 'create') {
-
- // Recommended size
- var size = newVMOSTypesObj[$(self.form).find('[name=newVMOSType]').val()].recommendedHDD;
-
- $.when(new vboxWizardNewHDDialog({'name':jQuery.trim($(self.form).find('[name=newVMName]').val()),'size':size,'group':vmgroup}).run())
- .done(function(med){
-
- $(self.form).find('[name=newVMDisk]').eq(2).trigger('click').prop('checked',true);
-
- // Add newly created disk as option and select it
- vmNewFillExistingDisks(med);
-
- vboxNewVMFinish();
-
- });
-
- return;
-
- } else if($(self.form).find('[name=newVMDisk]:checked').val() == 'none') {
-
- buttons = {};
- buttons[trans('Continue','UIMessageCenter')] = function(){
- $(this).empty().remove();
- vboxNewVMFinish();
- };
- vboxConfirm(trans('You are about to create a new virtual machine without a hard disk. You will not be able to install an operating system on the machine until you add one. In the mean time you will only be able to start the machine using a virtual optical disk or from the network.','UIMessageCenter'), buttons, trans('Go Back','UIMessageCenter'));
- return;
- }
-
- vboxNewVMFinish();
-
- };
- fe.run();
-
-
- },{'name':document.forms['frmwizardNewVM'].newVMName.value, 'group':vmgroup});
-
- fnl.run();
- };
-
-}
-
-/**
- * Run the Clone Virtual Machine Wizard
- * @param {Object} args - wizard data - args.vm and args.snapshot should be populated
- * @returns {Object} deferred promise
- * @see vboxWizard()
- */
-function vboxWizardCloneVMDialog(args) {
-
- // self reference
- var self = this;
-
- // Extend vboxWizard class
- this.parentClass = vboxWizard;
- this.parentClass();
-
- /* Common options */
- this.name = 'wizardCloneVM';
- this.title = trans('Clone Virtual Machine','UIWizardCloneVM');
- this.bg = 'images/vbox/vmw_clone_bg.png';
- this.icon = 'vm_clone';
- this.finishText = trans('Clone','UIWizardCloneVM');
- this.context = 'UIWizardCloneVM';
- this.widthAdvanced = 450;
- this.heightAdvanced = 350;
-
- /* Override run() because we need VM data */
- this.parentRun = this.run;
- this.run = function() {
-
- var l = new vboxLoader('vboxWizardCloneVMInit');
- l.showLoading();
- $.when(vboxVMDataMediator.getVMDetails(args.vm.id)).always(function() {
- // Always remove loading screen
- l.removeLoading();
- }).done(function(d){
- self.steps = (d.snapshotCount > 0 ? 3 : 2);
- self.args = $.extend(true,args,{'vm':d});
- self.parentRun();
- });
-
- return self.completed.promise();
-
- };
-
- /* Function to run when finished */
- this.onFinish = function() {
-
- // Get parameters
- var name = jQuery.trim($(self.form).find('[name=machineCloneName]').val());
- var src = self.args.vm.id;
- var snapshot = self.args.snapshot;
- var allNetcards = $(self.form).find('[name=vboxCloneReinitNetwork]').prop('checked');
-
- if(!name) {
- $(self.form).find('[name=machineCloneName]').addClass('vboxRequired');
- return;
- }
-
-
- // Only two steps? We can assume that state has no child states.
- // Force current state only
- var vmState = 'MachineState';
- if(self.steps > 2 || self.mode == 'advanced') {
- vmState = $(self.form).find('[name=vmState]:checked').val();
- }
-
- // Full / linked clone
- var cLink = $(self.form).find('[name=vboxCloneType]').eq(1).prop('checked');
-
- // wrap function
- var vbClone = function(sn) {
-
- $.when(vboxAjaxRequest('machineClone', {'name':name,'vmState':vmState,'src':src,'snapshot':sn,'reinitNetwork':allNetcards,'link':cLink}))
- .done(function(d){
- if(d.responseData.progress) {
- var registerVM = d.responseData.settingsFilePath;
- vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(ret) {
- $.when(vboxAjaxRequest('machineAdd',{'file':registerVM})).done(function(){
-
- $.when(vboxAjaxRequest('vboxGetMedia')).done(function(dat){
- $('#vboxPane').data('vboxMedia',dat.responseData);
- self.completed.resolve();
- }).fail(function(){
- self.completed.reject();
- });
- }).fail(function(){
- self.completed.reject();
- });
- },'progress_clone_90px.png',trans('Clone selected virtual machine','UIActionPool'),
- self.args.vm.name + ' > ' + name);
- } else {
- self.completed.reject();
- }
- }).fail(function(){
- self.completed.reject();
- });
- };
-
- // Check for linked clone, but not a snapshot
- if(cLink && !self.args.snapshot) {
-
- $.when(vboxAjaxRequest('snapshotTake',
- {'vm':src,'name':trans('Linked Base for %1 and %2','UIWizardCloneVM').replace('%1',self.args.vm.name).replace('%2',name),'description':''}))
- .done(function(d){
-
- if(d.responseData.progress) {
-
- vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(){
- $.when(vboxVMDataMediator.getVMDetails(src, true)).done(function(md){
- vbClone(md.currentSnapshot);
- });
- },
- 'progress_snapshot_create_90px.png',
- trans('Take a snapshot of the current virtual machine state','UIActionPool'),
- self.args.vm.name);
-
- } else if(d && d.error) {
- self.completed.reject();
- vboxAlert(d.error);
- }
-
- }).fail(function(){
- self.completed.reject();
- });
-
- // Just clone
- } else {
- vbClone(snapshot);
- }
-
- $(self.dialog).empty().remove();
-
- };
-}
-
-/**
- * Run the VM Log Viewer dialog
- * @param {String} vm - uuid or name of virtual machine to obtain logs for
- */
-function vboxShowLogsDialogInit(vm) {
-
- $('#vboxPane').append($('<div />').attr({'id':'vboxVMLogsDialog'}));
-
- var l = new vboxLoader();
- l.add('machineGetLogFilesList',function(r){
- $('#vboxVMLogsDialog').data({'logs':r.responseData.logs,'logpath':r.responseData.path});
- },{'vm':vm.id});
- l.addFileToDOM('panes/vmlogs.html',$('#vboxVMLogsDialog'));
- l.onLoad = function(){
- var buttons = {};
- buttons[trans('Refresh','UIVMLogViewer')] = function() {
- l = new vboxLoader();
- l.add('machineGetLogFilesList',function(r){
- $('#vboxVMLogsDialog').data({'logs':r.responseData.logs,'logpath':r.responseData.path});
-
- },{'vm':vm.id});
- l.onLoad = function(){
- vboxShowLogsInit(vm);
- };
- l.run();
- };
- buttons[trans('Close','UIVMLogViewer')] = function(){$(this).trigger('close').empty().remove();};
- $('#vboxVMLogsDialog').dialog({'closeOnEscape':true,'width':800,'height':500,'buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent','title':'<img src="images/vbox/vm_show_logs_16px.png" class="vboxDialogTitleIcon" /> '+ trans('%1 - VirtualBox Log Viewer','UIVMLogViewer').replace('%1',vm.name)}).on("dialogbeforeclose",function(){
- $(this).parent().find('span:contains("'+trans('Close','UIVMLogViewer')+'")').trigger('click');
- });
- vboxShowLogsInit(vm);
- };
- l.run();
-
-}
-
-/**
- * Show the Virtual Media Manager Dialog
- * @param {Boolean} select - true to display "Select" button and resolve with selected medium
- * @param {String} type - optionally restrict media to media of this type
- * @param {Boolean} hideDiff - optionally hide differencing HardDisk media
- * @param {String} mPath - optional path to use when adding or creating media through the VMM dialog
- * @returns {Object} deferred promise
- */
-function vboxVMMDialog(select,type,hideDiff,mPath) {
-
- var results = $.Deferred();
-
- $('#vboxPane').append($('<div />').attr({'id':'vboxVMMDialog','class':'vboxVMMDialog'}));
-
- var l = new vboxLoader();
- l.add('getConfig',function(d){$('#vboxPane').data('vboxConfig',d.responseData);});
- l.add('vboxSystemPropertiesGet',function(d){$('#vboxPane').data('vboxSystemProperties',d.responseData);});
- l.add('vboxGetMedia',function(d){$('#vboxPane').data('vboxMedia',d.responseData);});
- l.addFileToDOM('panes/vmm.html',$('#vboxVMMDialog'));
- l.onLoad = function() {
- var buttons = {};
- if(select) {
- buttons[trans('Select','UIMediumManager')] = function() {
- var sel = null;
- switch($("#vboxVMMTabs").tabs('option','active')) {
- case 0: /* HardDisks */
- sel = $('#vboxVMMHDList').find('tr.vboxListItemSelected').first();
- break;
- case 1: /* DVD */
- sel = $('#vboxVMMCDList').find('tr.vboxListItemSelected').first();
- break;
- default:
- sel = $('#vboxVMMFDList').find('tr.vboxListItemSelected').first();
- }
- if($(sel).length) {
- vboxMedia.updateRecent(vboxMedia.getMediumById($(sel).data('medium')));
- results.resolve($(sel).data('medium'));
- }
- $('#vboxVMMDialog').trigger('close').empty().remove();
- };
- }
- buttons[trans('Close','UIMessageCenter')] = function() {
- $('#vboxVMMDialog').trigger('close').empty().remove();
- results.reject();
- };
-
- $("#vboxVMMDialog").dialog({'closeOnEscape':true,'width':800,'height':500,'buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent vboxVMMDialog','title':'<img src="images/vbox/diskimage_16px.png" class="vboxDialogTitleIcon" /> '+trans('Virtual Media Manager','VBoxMediaManagerDlg')}).on("dialogbeforeclose",function(){
- $(this).parent().find('span:contains("'+trans('Close','UIMessageCenter')+'")').trigger('click');
- });
-
- vboxVMMInit(hideDiff,mPath);
-
- if(type) {
- switch(type) {
- case 'HardDisk':
- $("#vboxVMMTabs").tabs('option','active',0);
- $("#vboxVMMTabs").tabs('disable',1);
- $("#vboxVMMTabs").tabs('disable',2);
- break;
- case 'DVD':
- $("#vboxVMMTabs").tabs('option','active',1);
- $("#vboxVMMTabs").tabs('disable',0);
- $("#vboxVMMTabs").tabs('disable',2);
- break;
- case 'Floppy':
- $("#vboxVMMTabs").tabs('option','active',2);
- $("#vboxVMMTabs").tabs('disable',0);
- $("#vboxVMMTabs").tabs('disable',1);
- break;
- default:
- $("#vboxVMMTabs").tabs('option','active',0);
- break;
- }
- }
- };
- l.run();
-
- return results.promise();
-}
-
-/**
- * Run the New Virtual Disk wizard
- * @param {Object} suggested - sugggested defaults such as hard disk name and path
- */
-function vboxWizardNewHDDialog(suggested) {
-
- // reference
- var self = this;
-
- // Extend vboxWizard class
- this.parentClass = vboxWizard;
- this.parentClass();
-
- /* Common options */
- this.name = 'wizardNewHD';
- this.title = trans('Create Virtual Hard Disk','UIWizardNewVD');
- this.bg = 'images/vbox/vmw_new_harddisk_bg.png';
- this.icon = 'hd';
- this.steps = 3;
- this.suggested = suggested;
- this.context = 'UIWizardNewVD';
- this.finishText = trans('Create','UIWizardNewVD');
- this.height = 450;
-
- this.data = [
- {'fn':'vboxSystemPropertiesGet','callback':function(d){$('#vboxPane').data('vboxSystemProperties',d.responseData);}},
- {'fn':'vboxGetMedia','callback':function(d){$('#vboxPane').data('vboxMedia',d.responseData);}}
- ];
-
- // Compose folder if suggested name exists
- if(this.suggested && this.suggested.name) {
- if(!this.suggested['group']) this.suggested.group = '';
- this.data.push(
- {'fn':'vboxGetComposedMachineFilename','callback':function(d){
- self.suggested.path = vboxDirname(d.responseData)+$('#vboxPane').data('vboxConfig').DSEP;
- },'args':{'name':this.suggested.name, 'group':this.suggested.group}});
- }
-
- /* Function to run when wizard completes */
- this.onFinish = function() {
-
- // Fix size if we need to
- var mbytes = vboxConvertMbytes($(self.form).find('[name=wizardNewHDSizeValue]').val());
- $(self.form).find('[name=wizardNewHDSizeValue]').val(vboxMbytesConvert(mbytes));
- $('#wizardNewHDSizeLabel').html(vboxMbytesConvert(mbytes) + ' ('+mbytes+' '+trans('MB','VBoxGlobal')+')');
-
- // Determine file location
- var file = $(self.form).find('[name=wizardNewHDLocation]').val();
- if(file.search(/[\/|\\]/) < 0) {
- // just a name
- if(self.suggested.path) {
- if($('#vboxPane').data('vboxConfig').enforceVMOwnership==true){
- file = self.suggested.path + $('#vboxPane').data('vboxConfig').DSEP + $('#vboxPane').data('vboxSession').user + "_" + file;
- } else {
- file = self.suggested.path + $('#vboxPane').data('vboxConfig').DSEP + file;
- }
- } else{
- if($('#vboxPane').data('vboxConfig').enforceVMOwnership==true){
- file = $('#vboxPane').data('vboxSystemProperties').homeFolder + $('#vboxPane').data('vboxConfig').DSEP + $('#vboxPane').data('vboxSession').user + "_" + file;
- } else {
- file = $('#vboxPane').data('vboxSystemProperties').homeFolder + $('#vboxPane').data('vboxConfig').DSEP + file;
- }
- }
-
- // Enforce VM ownership
- } else if($('#vboxPane').data('vboxConfig').enforceVMOwnership==true) {
- // has user ownership so use folderbased
- var nameIndex = file.lastIndexOf($('#vboxPane').data('vboxConfig').DSEP);
- var path = file.substr(0,nameIndex);
- var name = file.substr(nameIndex+1,file.length);
- file = path +$('#vboxPane').data('vboxConfig').DSEP + $('#vboxPane').data('vboxSession').user + "_" + name;
- }
-
- var format = $(self.form)[0].elements['newHardDiskFileType'];
- var formatOpts = {};
- for(var i = 0; i < format.length; i++) {
- if(format[i].checked) {
- formatOpts = $(format[i]).closest('tr').data('vboxFormat');
- format=format[i].value;
- break;
- }
- }
-
- // append filename ext?
- if(jQuery.inArray(file.substring(file.lastIndexOf('.')+1).toLowerCase(),formatOpts.extensions) < 0) {
- file += '.'+formatOpts.extensions[0];
- }
-
- // Normalize file
- file = file.replace($('#vboxPane').data('vboxConfig').DSEP+$('#vboxPane').data('vboxConfig').DSEP,$('#vboxPane').data('vboxConfig').DSEP);
-
- /* Check to see if file exists */
- var fileExists = false;
- var l = new vboxLoader('fileExists');
- l.add('fileExists',function(d){
- fileExists = d.responseData;
- },{'file':file});
- l.onLoad = function() {
- if(fileExists) {
- vboxAlert(trans("<p>The hard disk storage unit at location <b>%1</b> already " +
- "exists. You cannot create a new virtual hard disk that uses this " +
- "location because it can be already used by another virtual hard " +
- "disk.</p>" +
- "<p>Please specify a different location.</p>",'UIMessageCenter').replace('%1',file));
- return;
- }
- var fsplit = $(self.form).find('[name=newHardDiskSplit]').prop('checked');
- var size = vboxConvertMbytes($(self.form).find('[name=wizardNewHDSizeValue]').val());
- var type = ($(self.form).find('[name=newHardDiskType]').eq(1).prop('checked') ? 'fixed' : 'dynamic');
- var nl = new vboxLoader('mediumCreateBaseStorage');
- nl.add('mediumCreateBaseStorage',function(d){
- if(d.responseData.progress) {
- vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(ret) {
- var ml = new vboxLoader();
- ml.add('vboxGetMedia',function(dat){$('#vboxPane').data('vboxMedia',dat.responseData);});
- ml.onLoad = function() {
- var med = vboxMedia.getMediumByLocation(file);
- if(med) {
- vboxMedia.updateRecent(med);
- self.completed.resolve(med.id);
- } else {
- self.completed.reject();
- }
- };
- ml.run();
- },'progress_media_create_90px.png',trans('Create a virtual hard disk now','UIWizardNewVM'),
- vboxBasename(file),true);
- } else {
- self.completed.reject();
- }
- },{'file':file,'type':type,'size':size,'format':format,'split':fsplit});
- nl.run();
-
- $(self.dialog).empty().remove();
- };
- l.run();
-
- };
-}
-
-/**
- * Run the Copy Virtual Disk wizard
- * @param {Object} suggested - sugggested defaults such as hard disk name and path
- */
-function vboxWizardCopyHDDialog(suggested) {
-
- // reference
- var self = this;
-
- /* Extend vboxWizard class */
- this.parentClass = vboxWizard;
- this.parentClass();
-
- /* Common options */
- this.name = 'wizardCopyHD';
- this.title = trans('Copy Virtual Hard Disk','UIWizardCloneVD');
- this.bg = 'images/vbox/vmw_new_harddisk_bg.png';
- this.icon = 'hd';
- this.steps = 4;
- this.suggested = suggested;
- this.context = 'UIWizardCloneVD';
- this.finishText = trans('Copy','UIWizardCloneVD');
- this.height = 450;
-
- this.data = [
- {'fn':'vboxSystemPropertiesGet','callback':function(d){$('#vboxPane').data('vboxSystemProperties',d.responseData);}},
- {'fn':'vboxGetMedia','callback':function(d){$('#vboxPane').data('vboxMedia',d.responseData);}}
- ];
-
-
- /* Function run when wizard completes */
- this.onFinish = function() {
-
- var format = $(self.form)[0].elements['copyHDFileType'];
- var formatOpts = {};
- for(var i = 0; i < format.length; i++) {
- if(format[i].checked) {
- formatOpts = $(format[i]).closest('tr').data('vboxFormat');
- break;
- }
- }
-
- var src = $(self.form).find('[name=copyHDDiskSelect]').val();
- var type = ($(self.form).find('[name=newHardDiskType]').eq(1).prop('checked') ? 'fixed' : 'dynamic');
- var format = $(self.form)[0].elements['copyHDFileType'];
- for(var i = 0; i < format.length; i++) {
- if(format[i].checked) {
- format=format[i].value;
- break;
- }
- }
-
- var fsplit = $(self.form).find('[name=newHardDiskSplit]').prop('checked') && vboxMedia.formatSupportsSplit(format);
-
- var loc = jQuery.trim($(self.form).find('[name=wizardCopyHDLocation]').val());
- if(!loc) {
- $(self.form).find('[name=wizardCopyHDLocation]').addClass('vboxRequired');
- return;
- }
- $(self.form).find('[name=wizardCopyHDLocation]').removeClass('vboxRequired');
- if(loc.search(/[\/|\\]/) < 0) {
- if($('#wizardCopyHDStep4').data('suggestedpath')) {
- loc = $('#wizardCopyHDStep4').data('suggestedpath') + loc;
- } else {
- loc = vboxDirname(vboxMedia.getMediumById(src).location) + $('#vboxPane').data('vboxConfig').DSEP + loc;
- }
- }
-
- // append ext?
- if(jQuery.inArray(loc.substring(loc.lastIndexOf('.')+1).toLowerCase(),formatOpts.extensions) < 0) {
- loc += '.'+formatOpts.extensions[0];
- }
-
-
- /* Check to see if file exists */
- var fileExists = false;
- var fe = new vboxLoader();
- fe.add('fileExists',function(d){
- fileExists = d.responseData;
- },{'file':loc});
- fe.onLoad = function() {
- if(fileExists) {
- vboxAlert(trans("<p>The hard disk storage unit at location <b>%1</b> already " +
- "exists. You cannot create a new virtual hard disk that uses this " +
- "location because it can be already used by another virtual hard " +
- "disk.</p>" +
- "<p>Please specify a different location.</p>",'UIMessageCenter').replace('%1',loc));
- return;
- }
- $(self.dialog).empty().remove();
-
- var l = new vboxLoader();
- l.add('mediumCloneTo',function(d){
- if(d.responseData.progress) {
- vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(ret,mid) {
- var ml = new vboxLoader();
- ml.add('vboxGetMedia',function(dat){$('#vboxPane').data('vboxMedia',dat.responseData);});
- ml.onLoad = function() {
- med = vboxMedia.getMediumByLocation(loc);
- vboxMedia.updateRecent(med);
- self.completed.resolve(mid);
- };
- ml.run();
- },'progress_media_create_90px.png',trans('Copy Virtual Hard Disk','UIWizardCloneVD'),
- vboxBasename(vboxMedia.getMediumById(src).location) + ' > ' + vboxBasename(loc));
- } else {
- self.completed.reject();
- }
- },{'src':vboxMedia.getMediumById(src).location,'type':type,'format':format,'location':loc,'split':fsplit});
- l.run();
- };
- fe.run();
-
-
- };
-}
-
-/**
- * Display guest network adapters dialog
- * @param {String} vm - virtual machine uuid or name
- */
-function vboxGuestNetworkAdaptersDialogInit(vm) {
-
- /*
- * Dialog
- */
- $('#vboxPane').append($('<div />').attr({'id':'vboxGuestNetworkDialog','style':'display: none'}));
-
- /*
- * Loader
- */
- var l = new vboxLoader();
- l.addFileToDOM('panes/guestNetAdapters.html',$('#vboxGuestNetworkDialog'));
- l.onLoad = function(){
-
- var buttons = {};
- buttons[trans('Close','UIVMLogViewer')] = function() {$('#vboxGuestNetworkDialog').trigger('close').empty().remove();};
- $('#vboxGuestNetworkDialog').dialog({'closeOnEscape':true,'width':500,'height':250,'buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent','title':'<img src="images/vbox/nw_16px.png" class="vboxDialogTitleIcon" /> ' + trans('Guest Network Adapters','VBoxGlobal')}).on("dialogbeforeclose",function(){
- $(this).parent().find('span:contains("'+trans('Close','UIVMLogViewer')+'")').trigger('click');
- });
-
- // defined in pane
- vboxVMNetAdaptersInit(vm,nic);
- };
- l.run();
-
-}
-
-
-/**
- * Display Global Preferences dialog
- */
-
-function vboxGlobalPrefsDialog() {
-
- // Prefs
- var panes = new Array(
- {'name':'GlobalGeneral','label':'General','icon':'machine','context':'UIGlobalSettingsGeneral'},
- {'name':'GlobalLanguage','label':'Language','icon':'site','context':'UIGlobalSettingsLanguage'},
- {'name':'GlobalNetwork','label':'Network','icon':'nw','context':'UIGlobalSettingsNetwork','tabbed':true},
- {'name':'GlobalUsers','label':'Users','icon':'register','context':'UIUsers'}
- );
-
- var data = new Array(
- {'fn':'hostOnlyInterfacesGet','callback':function(d){$('#vboxSettingsDialog').data('vboxHostOnlyInterfaces',d.responseData);}},
- {'fn':'vboxSystemPropertiesGet','callback':function(d){$('#vboxSettingsDialog').data('vboxSystemProperties',d.responseData);}},
- {'fn':'vboxNATNetworksGet','callback':function(d){$('#vboxSettingsDialog').data('vboxNATNetworks',d.responseData);}},
- {'fn':'getUsers','callback':function(d){$('#vboxSettingsDialog').data('vboxUsers',d.responseData);}}
- );
-
- // Check for noAuth setting
- if($('#vboxPane').data('vboxConfig').noAuth || !$('#vboxPane').data('vboxSession').admin || !$('#vboxPane').data('vboxConfig').authCapabilities.canModifyUsers) {
- panes.pop();
- data.pop();
- }
-
- $.when(vboxSettingsDialog(trans('Preferences...','UIActionPool').replace(/\./g,''),
- panes,data,null,'global_settings','UISettingsDialogGlobal'))
- .done(function(){
-
- var l = new vboxLoader();
-
- // Language change?
- if($('#vboxSettingsDialog').data('language') && $('#vboxSettingsDialog').data('language') != __vboxLangName) {
- vboxSetCookie('vboxLanguage',$('#vboxSettingsDialog').data('language'));
- l.onLoad = function(){location.reload(true);};
-
- }
-
- l.add('vboxNATNetworksSave',function(){return;},{'networks':$('#vboxSettingsDialog').data('vboxNATNetworks')});
- l.add('hostOnlyInterfacesSave',function(){return;},{'networkInterfaces':$('#vboxSettingsDialog').data('vboxHostOnlyInterfaces').networkInterfaces});
- l.add('vboxSystemPropertiesSave',function(){return;},{'SystemProperties':$('#vboxSettingsDialog').data('vboxSystemProperties')});
- l.run();
-
- // Update system properties
- $('#vboxPane').data('vboxSystemProperties',$('#vboxSettingsDialog').data('vboxSystemProperties'));
-
- });
-
-}
-
-
-
-/**
- * Display a virtual machine settings dialog
- * @param {String} vm - uuid or name of virtual machine
- * @param {String} pane - optionally automatically select pane when dialog is displayed
- * @returns {Object} deferred promise
- */
-function vboxVMsettingsDialog(vm,pane) {
-
- var results = $.Deferred();
-
- if(typeof(vm) == 'string')
- vm = vboxVMDataMediator.getVMData(vm);
-
- // Only show these dialogs once per change
- var reloadConfirmShowing = false;
-
- // Handler for when VM settings have changed
- /////////////////////////////////////////////
- var machineSettingsChanged = function(e, eventList) {
-
- for(var i = 0; i < eventList.length; i++) {
-
- ////////////////////////////////
- //
- // Machine data changed..
- //
- ////////////////////////////////
- switch(eventList[i].eventType) {
-
- case 'OnMachineStateChanged':
-
- if(!eventList[i].machineId || eventList[i].machineId != vm.id) break;
-
- // Display loading screen
- var l = new vboxLoader();
- l.showLoading();
-
- $.when(vboxVMDataMediator.getVMDataCombined(vm.id)).done(function(vmData) {
- // data received from deferred object
- $('#vboxSettingsDialog').data('vboxMachineData',vmData);
- $('#vboxSettingsDialog').data('vboxFullEdit', (vboxVMStates.isPoweredOff(vmData) && !vboxVMStates.isSaved(vmData)));
- $('#vboxSettingsDialog').trigger('dataLoaded');
- l.removeLoading();
- if(vboxVMStates.isRunning(vmData)) {
- vboxAlert(trans('The virtual machine that you are changing has been started. Only certain settings can be changed while a machine is running. All other changes will be lost if you close this window now.','UIMessageCenter'));
- }
- });
-
- break;
-
- // Unregistered machine
- case 'OnMachineRegistered':
-
- if(!eventList[i].machineId || eventList[i].machineId != vm.id || eventList[i].registered) break;
-
- $('#vboxSettingsDialog').parent().find('span:contains("'+trans('Cancel','QIMessageBox')+'")').trigger('click');
- break;
-
- case 'OnMachineDataChanged':
- case 'OnNetworkAdapterChanged':
- case 'OnVRDEServerInfoChanged':
- case 'OnCPUChanged':
- case 'OnStorageControllerChanged':
- case 'OnMediumChanged':
- case 'OnVRDEServerChanged':
- case 'OnUSBControllerChanged':
- case 'OnSharedFolderChanged':
- case 'OnCPUExecutionCapChanged':
- case 'OnStorageDeviceChanged':
- case 'OnNATRedirect':
-
- if(!eventList[i].machineId || eventList[i].machineId != vm.id) break;
-
- // already showing reload confirmation
- if(reloadConfirmShowing) break;
-
- var buttons = {};
- buttons[trans('Reload settings','UIMessageCenter')] = function() {
-
- // Display loading screen
- var l = new vboxLoader();
- l.showLoading();
-
- $(this).empty().remove();
-
- /*
- * Data to be reloaded
- */
- var reload = [
- vboxAjaxRequest('vboxGetMedia',{}).done(function(d){$('#vboxPane').data('vboxMedia',d.responseData);}),
-
- vboxAjaxRequest('getNetworking',{}).done(function(d){$('#vboxSettingsDialog').data('vboxNetworking',d.responseData);}),
-
- vboxAjaxRequest('vboxRecentMediaGet',{}).done(function(d){$('#vboxPane').data('vboxRecentMedia',d.responseData);}),
-
- vboxAjaxRequest('consoleGetSharedFolders',{'vm':vm.id}).done(function(d){$('#vboxSettingsDialog').data('vboxTransientSharedFolders',d.responseData);}),
-
- $.when(vboxVMDataMediator.getVMDataCombined(vm.id)).done(function(vmData) {
-
- // data received from deferred object
- $('#vboxSettingsDialog').data('vboxMachineData',vmData);
- $('#vboxSettingsDialog').data('vboxFullEdit', (vboxVMStates.isPoweredOff(vmData) && !vboxVMStates.isSaved(vmData)));
-
- })
- ];
-
- // Only when all of these are done
- $.when.apply($, reload).done(function(){
-
- /* Change title and tell dialog that data is loaded */
- $('#vboxSettingsDialog').trigger('dataLoaded').dialog('option','title','<img src="images/vbox/vm_settings_16px.png" class="vboxDialogTitleIcon" /> ' +
- $('<div />').text($('#vboxSettingsDialog').data('vboxMachineData').name).text() + ' - ' + trans('Settings','UISettingsDialog'));
-
- l.removeLoading();
- reloadConfirmShowing = false;
- });
-
-
- };
-
- reloadConfirmShowing = true;
-
- vboxConfirm(trans("<p>The machine settings were changed while you were editing them. You currently have unsaved setting changes.</p><p>Would you like to reload the changed settings or to keep your own changes?</p>",'UIMessageCenter'),
- buttons,
- trans('Keep changes', 'UIMessageCenter'), function(){
- reloadConfirmShowing = false;
- });
-
- return;
-
- }
- }
- };
-
- // Watch for changed VM settings
- $('#vboxPane').on('vboxEvents',machineSettingsChanged);
-
- $.when(vboxVMDataMediator.getVMDataCombined(vm.id)).done(function(vmData) {
-
-
- /*
- * Settings dialog data
- */
- var dataList = new Array(
- {'fn':'vboxGetMedia','callback':function(d){
-
- $('#vboxPane').data('vboxMedia',d.responseData);
-
- // data received from deferred object
- $('#vboxSettingsDialog').data('vboxMachineData',vmData);
- $('#vboxSettingsDialog').data('vboxFullEdit', (vboxVMStates.isPoweredOff(vmData) && !vboxVMStates.isSaved(vmData)));
-
- }},
- {'fn':'getNetworking','callback':function(d){$('#vboxSettingsDialog').data('vboxNetworking',d.responseData);}},
- {'fn':'hostGetDetails','callback':function(d){$('#vboxSettingsDialog').data('vboxHostDetails',d.responseData);}},
- {'fn':'vboxGetEnumerationMap','callback':function(d){$('#vboxSettingsDialog').data('vboxNetworkAdapterTypes',d.responseData);},'args':{'class':'NetworkAdapterType'}},
- {'fn':'vboxGetEnumerationMap','callback':function(d){$('#vboxSettingsDialog').data('vboxAudioControllerTypes',d.responseData);},'args':{'class':'AudioControllerType'}},
- {'fn':'vboxRecentMediaGet','callback':function(d){$('#vboxPane').data('vboxRecentMedia',d.responseData);}},
- {'fn':'consoleGetSharedFolders','callback':function(d){$('#vboxSettingsDialog').data('vboxTransientSharedFolders',d.responseData);},'args':{'vm':vm.id}}
- );
-
- /*
- * Settings dialog panes
- */
- var panes = new Array(
-
- {name:'General',label:'General',icon:'machine',tabbed:true,context:'UIMachineSettingsGeneral'},
- {name:'System',label:'System',icon:'chipset',tabbed:true,context:'UIMachineSettingsSystem'},
- {name:'Display',label:'Display',icon:'vrdp',tabbed:true,context:'UIMachineSettingsDisplay'},
- {name:'Storage',label:'Storage',icon:'attachment',context:'UIMachineSettingsStorage'},
- {name:'Audio',label:'Audio',icon:'sound',context:'UIMachineSettingsAudio'},
- {name:'Network',label:'Network',icon:'nw',tabbed:true,context:'UIMachineSettingsNetwork'},
- {name:'SerialPorts',label:'Serial Ports',icon:'serial_port',tabbed:true,context:'UIMachineSettingsSerial'},
- {name:'ParallelPorts',label:'Parallel Ports',icon:'parallel_port',tabbed:true,disabled:(!$('#vboxPane').data('vboxConfig').enableLPTConfig),context:'UIMachineSettingsParallel'},
- {name:'USB',label:'USB',icon:'usb',context:'UIMachineSettingsUSB'},
- {name:'SharedFolders',label:'Shared Folders',icon:'sf',context:'UIMachineSettingsSF'}
-
- );
-
- /*
- * Check for encryption settings change
- */
- var presaveCallback = function() {
-
- if(!$('#vboxSettingsDialog').data('vboxEncSettingsChanged'))
- return true;
-
- var encMediaSettings = $.Deferred();
-
- // Validate
- if(!vboxSettingsGeneralValidate()) {
- $('#vboxSettingsMenuList').children('li:eq(0)').first().click();
- $('#vboxSettingsPane-General').tabs('option','active', 3);
- encMediaSettings.reject();
- return encMediaSettings;
- }
-
- var vm = $('#vboxSettingsDialog').data('vboxMachineData');
- var media = vboxStorage.getAttachedBaseMedia(vm);
- var encIds = vboxMedia.getEncryptedMediaIds(media);
- var encMedia = vboxMedia.getEncryptedMedia(media);
-
- var formCipher = $('#vboxSettingsDialog').data('vboxEncCipher');
- var formPassword = $('#vboxSettingsDialog').data('vboxEncPw');
- var formEncEnabled = $('#vboxSettingsDialog').data('vboxEncEnabled');
-
- // If encryption is not enabled, cipher needs to be blank
- if(!formEncEnabled) {
- formCipher = '';
- }
-
- // Get encryption password(s)
- $.when(vboxMediumEncryptionPasswordsDialog(vm.name, encIds))
-
- .done(function(pwdata) {
-
- var runs = []
-
- // Each medium attached
- for(var i = 0; i < media.length; i++) {
- // Only hard disks
- if(media[i].deviceType != 'HardDisk') continue;
-
- var id = vm.name;
- var oldpw = "";
- var cipher = null;
-
- // Check for existing encryption setting
- for(var a = 0; a < encMedia.length; a++) {
- if(encMedia[a].medium == media[i].id) {
- cipher = media[i].encryptionSettings.cipher;
- // Look in passwords for id
- for(var b = 0; b < pwdata.length; b++) {
- if(pwdata[b].id == id) {
- oldpw = pwdata[b].password;
- break;
- }
- }
- break;
- }
- }
-
- runs.push({
- medium: media[i].id,
- cipher: cipher,
- encId: id,
- password: oldpw
- });
-
- }
- // No encrypted media changes
- if(!runs.length) {
- encMediaSettings.resolve();
- return;
- }
-
- var l = new vboxLoader();
- l.showLoading();
-
- (function doruns(encMediaRuns){
-
- if(!encMediaRuns.length) {
- l.removeLoading();
- encMediaSettings.resolve();
- return;
- }
-
- var run = encMediaRuns.shift();
-
- // If encryption is enabled, and cypher is blank / "Leave Unchanged"
- // keep the original cipher
- var mcipher = formCipher;
- if(formEncEnabled && !mcipher) {
- mcipher = run.cipher;
- }
-
- var rdata = {
- medium: run.medium,
- id: run.encId,
- old_password: run.password,
- cipher: mcipher,
- password: formPassword
- };
-
- $.when(vboxAjaxRequest('mediumChangeEncryption',rdata)).done(function(d){
-
- if(d.responseData.progress) {
- var icon = 'progress_media_create_90px.png';
- var title = trans('Encryption');
- vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(){
- // Loop
- doruns(encMediaRuns);
- },icon,title,vboxMedia.getMediumById(run.medium).name, true);
- } else {
- l.removeLoading();
- encMediaSettings.reject();
- return;
- }
-
- });
-
- })(runs);
-
- })
- .fail(function() {
- encMediaSettings.reject();
- });
-
- return encMediaSettings.promise();
-
- }
-
- $.when(vboxSettingsDialog(vmData.name + ' - ' + trans('Settings','UISettingsDialog'),panes,dataList,pane,'vm_settings','UISettingsDialogMachine', presaveCallback))
-
- // Always run this
- .always(function(){
-
- // No longer watch for changed VM settings
- $('#vboxPane').unbind('vboxEvents',machineSettingsChanged);
-
- })
-
- // Run this when "Save" is clicked
- .done(function() {
-
- var loader = new vboxLoader();
- var sdata = $.extend($('#vboxSettingsDialog').data('vboxMachineData'),{'clientConfig':$('#vboxPane').data('vboxConfig')});
- loader.add('machineSave',function(){return;},sdata);
- loader.onLoad = function() {
- // Refresh media
- var mload = new vboxLoader();
- mload.add('vboxGetMedia',function(d){$('#vboxPane').data('vboxMedia',d.responseData);});
- mload.onLoad = function() {
- results.resolve();
- };
- mload.run();
- };
- loader.run();
- });
-
- });
-
- return results.promise();
-}
-
-
-
-
-/**
- * Run the "First Run" wizard
- * @param {Object} vm - VM details object
- */
-function vboxWizardFirstRunDialog(vm) {
-
- // ref
- var self = this;
-
- this.parentClass = vboxWizard;
- this.parentClass();
-
- this.name = 'wizardFirstRun';
- this.title = $('<div />').text(vm.name).html();
- this.bg = 'images/vbox/vmw_first_run_bg.png';
- this.icon = vboxGuestOSTypeIcon(vm.OSTypeId);
- this.steps = 1;
- this.finishText = trans('Start','UIWizardFirstRun');
- this.context = 'UIWizardFirstRun';
- this.noAdvanced = true;
- this.args = vm;
-
- // This still resolves on cancel
- this.onCancel = function () {
- self.completed.resolve();
- };
-
- this.onFinish = function() {
-
- var med = vboxMedia.getMediumById($('#wizardFirstRunMedia').find(":selected").attr('value'));
-
- $(self.dialog).empty().remove();
-
- if(med) {
-
- var port = null;
- var device = null;
- var bus = null;
- var controller = null;
-
- for(var i = 0; i < self.args.storageControllers.length; i++) {
- for(var a = 0; a < self.args.storageControllers[i].mediumAttachments.length; a++) {
- if(self.args.storageControllers[i].mediumAttachments[a].type == "DVD" &&
- self.args.storageControllers[i].mediumAttachments[a].medium == null) {
-
- port = self.args.storageControllers[i].mediumAttachments[a].port;
- device = self.args.storageControllers[i].mediumAttachments[a].device;
- bus = self.args.storageControllers[i].bus;
- controller = self.args.storageControllers[i].name;
-
- break;
- }
- }
- }
-
-
- var args = {'vm':self.args.id,
- 'medium':med,
- 'port':port,
- 'device':device,
- 'bus':bus,
- 'controller':controller,
- 'noSave':true
- };
-
- // Ajax request to mount medium
- var mount = new vboxLoader();
- mount.add('mediumMount',function(ret){
- var l = new vboxLoader();
- l.add('vboxGetMedia',function(d){$('#vboxPane').data('vboxMedia',d.responseData);});
- l.onLoad = function(){
- self.completed.resolve();
- };
- l.run();
- },args);
- mount.run();
-
-
- } else {
- self.completed.resolve();
- }
-
- };
-}
-
-
-/**
- * Display a settings dialog (generic) called by dialog initializers
- * @param {String} title - title of dialog
- * @param {Array} panes - list of panes {Object} to load
- * @param {Object} data - list of data to load
- * @param {String} pane - optionally automatically select pane when dialog is shown
- * @param {String} icon - optional URL to icon for dialog
- * @param {String} langContext - language context to use for translations
- * @param {Function} presave - presave callback to run
- * @returns {Object} deferred promise
- * @see trans()
- */
-function vboxSettingsDialog(title,panes,data,pane,icon,langContext,presave) {
-
- var results = $.Deferred();
-
- var d = $('<div />').attr({'id':'vboxSettingsDialog','style':'display: none;'});
-
- var f = $('<form />').attr({'name':'frmVboxSettings','style':'height: 100%'});
-
- var t = $('<table />').attr({'style':'height: 100%;','class':'vboxSettingsTable'});
-
- var tr = $('<tr />');
-
- $($('<td />').attr({'id':'vboxSettingsMenu','style': (panes.length == 1 ? 'display:none;' : '')})).append($('<ul />').attr({'id':'vboxSettingsMenuList','class':'vboxHover'})).appendTo(tr);
-
- var td = $('<td />').attr({'id':'vboxSettingsPane'}).css({'height':'100%'});
-
- // Settings table contains title and visible settings pane
- var stbl = $('<table />').css({'height':'100%','width':'100%','padding':'0px','margin':'0px','border':'0px','border-spacing':'0px'});
-
- // Title
- var d1 = $('<div />').attr({'id':'vboxSettingsTitle'}).html('Padding').css({'display':(panes.length == 1 ? 'none' : '')});
- $(stbl).append($('<tr />').append($('<td />').css({'height':'1%','padding':'0px','margin':'0px','border':'0px'}).append(d1)));
-
-
- // Settings pane
- var d1 = $('<div />').attr({'id':'vboxSettingsList'}).css({'width':'100%'});
-
- $(stbl).append($('<tr />').append($('<td />').css({'padding':'0px','margin':'0px','border':'0px'}).append(d1)));
-
-
- $(td).append(stbl).appendTo(tr);
-
- $(d).append($(f).append($(t).append(tr))).appendTo('#vboxPane');
-
- /* Load panes and data */
- var loader = new vboxLoader();
-
- /* Load Data */
- for(var i = 0; i < data.length; i++) {
- loader.add(data[i].fn,data[i].callback,(data[i].args ? data[i].args : undefined));
- }
-
- /* Load settings panes */
- for(var i = 0; i < panes.length; i++) {
-
- if(panes[i].disabled) continue;
-
- // Menu item
- $('<li />').html('<div><img src="images/vbox/'+panes[i].icon+'_16px.png" /></div> <div>'+trans(panes[i].label,langContext)+'</div>').data(panes[i]).click(function(){
-
- $('#vboxSettingsTitle').html(trans($(this).data('label'),langContext));
-
- $(this).addClass('vboxListItemSelected').siblings().addClass('vboxListItem').removeClass('vboxListItemSelected');
-
- // jquery apply this css to everything with class .settingsPa..
- $('#vboxSettingsDialog .vboxSettingsPaneSection').css({'display':'none'});
-
- // Show selected pane
- $('#vboxSettingsPane-' + $(this).data('name')).css({'display':''}).children().first().trigger('show');
-
- }).on("mouseenter",function(){$(this).addClass('vboxHover');}).on("mouseleave",function(){$(this).removeClass('vboxHover');}).appendTo($('#vboxSettingsMenuList'));
-
-
- // Settings pane
- $('#vboxSettingsList').append($('<div />').attr({'id':'vboxSettingsPane-'+panes[i].name,'style':'display: none;','class':'vboxSettingsPaneSection ui-corner-all ' + (panes[i].tabbed ? 'vboxTabbed' : 'vboxNonTabbed')}));
-
- loader.addFileToDOM('panes/settings'+panes[i].name+'.html',$('#vboxSettingsPane-'+panes[i].name));
-
- }
-
- loader.onLoad = function(){
-
-
- /* Init UI Items */
- for(var i = 0; i < panes.length; i++) {
- vboxInitDisplay($('#vboxSettingsPane-'+panes[i].name),panes[i].context);
- if(panes[i].tabbed) $('#vboxSettingsPane-'+panes[i].name).tabs();
- }
-
- /* Tell dialog that data is loaded */
- $('#vboxSettingsDialog').trigger('dataLoaded');
-
- var buttons = { };
- buttons[trans('OK','QIMessageBox')] = function() {
-
- $(this).trigger('save');
-
- // Does some settings pane need to do some presave
- // work? (ask questions, run wizard, some other asynch task)
- var promise = true;
- if(presave) {
- promise = presave();
- }
- var dlg = this;
- $.when(promise).done(function() {
- results.resolve(true);
- $(dlg).trigger('close').empty().remove();
- $(document).trigger('click');
- });
- };
- buttons[trans('Cancel','QIMessageBox')] = function() {
- results.reject();
- $(this).trigger('close').empty().remove();
- $(document).trigger('click');
- };
-
- // Init with "nothing has changed yet"
- $('#vboxSettingsDialog').data('formDataChanged', false);
-
- // Show dialog
- $('#vboxSettingsDialog').dialog({'closeOnEscape':true,'width':(panes.length > 1 ? 900 : 600),'height':(panes.length > 1 ? 500 : 450),'buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxSettingsDialog vboxDialogContent','title':(icon ? '<img src="images/vbox/'+icon+'_16px.png" class="vboxDialogTitleIcon" /> ' : '') + title}).on("dialogbeforeclose",function(){
- $(this).parent().find('span:contains("'+trans('Cancel','QIMessageBox')+'")').trigger('click');
- });
-
- // Resize pane
- $('#vboxSettingsList').height($('#vboxSettingsList').parent().innerHeight()-8).css({'overflow':'auto','padding':'0px','margin-top':'8px','border':'0px','border-spacing':'0px'});
-
- // Resizing dialog, resizes this too
- $('#vboxSettingsDialog').on('dialogresizestop',function(){
- var h = $('#vboxSettingsList').css({'display':'none'}).parent().innerHeight();
- $('#vboxSettingsList').height(h-8).css({'display':''});
- });
-
- /* Select first or passed menu item */
- var i = 0;
- var offset = 0;
- var tab = undefined;
- if(typeof pane == "string") {
- var section = pane.split(':');
- if(section[1]) tab = section[1];
- for(i = 0; i < panes.length; i++) {
- if(panes[i].disabled) offset++;
- if(panes[i].name == section[0]) break;
- }
- }
- i-=offset;
- if(i >= panes.length) i = 0;
- $('#vboxSettingsMenuList').children('li:eq('+i+')').first().click().each(function(){
- if(tab !== undefined) {
- // Check for out of scope tab
- tab = Math.min(($('#vboxSettingsPane-'+$(this).data('name')).children('ul').first().children().length-1), parseInt(tab));
- $('#vboxSettingsPane-'+$(this).data('name')).tabs('option','active', tab);
- }
-
- });
-
- /* Only 1 pane? */
- if(panes.length == 1) {
- $('#vboxSettingsDialog table.vboxSettingsTable').css('width','100%');
- $('#vboxSettingsDialog').dialog('option','title',(icon ? '<img src="images/vbox/'+icon+'_16px.png" class="vboxDialogTitleIcon" /> ' : '') + trans(panes[0].label,langContext));
- }
-
-
- };
-
- loader.run();
-
- return results.promise();
-
-}
+/** + * @fileOverview Dialog logic for various wizards and other dialogs + * @author Ian Moore (imoore76 at yahoo dot com) + * @version $Id: dialogs.js 599 2015-07-27 10:40:37Z imoore76 $ + * @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com) + */ + +/** + * Run the import appliance wizard + */ +function vboxWizardImportApplianceDialog() { + + // reference + var self = this; + + // Extend vboxWizard + this.parentClass = vboxWizard; + this.parentClass(); + + /* Common settings */ + this.name = 'wizardImportAppliance'; + this.title = trans('Import Virtual Appliance','UIWizardImportApp'); + this.bg = 'images/vbox/vmw_ovf_import_bg.png'; + this.icon = 'import'; + this.steps = 2; + this.height = 500; + this.finishText = trans('Import','UIWizardImportApp'); + this.context = 'UIWizardImportApp'; + + /* Restore defaults is added to last step */ + this.stepButtons = [ + { + 'name' : trans('Restore Defaults','UIWizardImportApp'), + 'steps' : [-1], + 'click' : function() { + wizardImportApplianceParsed(); + } + } + ]; + + /* Data to be loaded */ + this.data = [ + {'fn':'vboxGetEnumerationMap','args':{'class':'NetworkAdapterType'},'callback':function(d){$('#vboxPane').data('vboxNetworkAdapterTypes',d.responseData);}}, + {'fn':'vboxGetEnumerationMap','args':{'class':'AudioControllerType'},'callback':function(d){$('#vboxPane').data('vboxAudioControllerTypes',d.responseData);}}, + ]; + + /* Perform action on finish */ + this.onFinish = function() { + + var file = $(self.form).find('[name=wizardImportApplianceLocation]').val(); + var descriptions = $('#vboxImportProps').data('descriptions'); + var reinitNetwork = $(self.form).find('[name=vboxImportReinitNetwork]').prop('checked'); + + // Check for descriptions + if(!descriptions) { + return; + } + + + /** Call Appliance import AJAX function */ + var vboxImportApp = function() { + + $(self.dialog).empty().remove(); + + var l = new vboxLoader(); + l.add('applianceImport',function(d){ + if(d.responseData.progress) { + vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(){ + // Imported media must be refreshed + var ml = new vboxLoader(); + ml.add('vboxGetMedia',function(d){$('#vboxPane').data('vboxMedia',d.responseData);}); + ml.onLoad = function(){ + self.completed.resolve(); + }; + ml.run(); + },'progress_import_90px.png',trans('Import an appliance into VirtualBox','UIActionPool').replace(/\.+$/g,''),vboxBasename(file)); + } else { + self.completed.reject(); + } + },{'descriptions':descriptions,'file':file,'reinitNetwork':reinitNetwork}); + l.run(); + }; + + // license agreements + var licenses = []; + + // Step through each VM and obtain value + for(var a = 0; a < descriptions.length; a++) { + var children = $('#vboxImportProps').children('tr.vboxChildOf'+a); + descriptions[a][5] = []; // enabled / disabled + var lic = null; + var vmname = null; + for(var b = 0; b < children.length; b++) { + descriptions[a][5][b] = !$(children[b]).data('propdisabled'); + descriptions[a][3][$(children[b]).data('descOrder')] = $(children[b]).children('td:eq(1)').data('descValue'); + // check for license + if(descriptions[a][0][b] == 'License') { + lic = descriptions[a][2][b]; + } else if(descriptions[a][0][b] == 'Name') { + vmname = descriptions[a][2][b]; + } + } + if(lic) { + if(!vmname) vmname = trans('Virtual System %1','UIApplianceEditorWidget').replace('%1',a); + licenses[licenses.length] = {'name':vmname,'license':lic}; + } + } + + + if(licenses.length) { + + var msg = trans('<b>The virtual system "%1" requires that you agree to the terms and conditions of the software license agreement shown below.</b><br /><br />Click <b>Agree</b> to continue or click <b>Disagree</b> to cancel the import.','UIImportLicenseViewer'); + var a = 0; + var buttons = {}; + buttons[trans('Agree','UIImportLicenseViewer')] = function() { + + a++; + if(a >= licenses.length) { + $(this).remove(); + vboxImportApp(); + return; + } + $(this).dialog('close'); + $('#vboxImportWizLicTitle').html(msg.replace('%1',licenses[a]['name'])); + $('#vboxImportWizLicContent').val(licenses[a]['license']); + $(this).dialog('open'); + + }; + buttons[trans('Disagree','UIImportLicenseViewer')] = function() { + $(this).empty().remove(); + }; + + var dlg = $('<div />').dialog({'closeOnEscape':false,'width':600,'height':500,'buttons':buttons,'modal':true,'autoOpen':false,'dialogClass':'vboxDialogContent vboxWizard','title':'<img src="images/vbox/os_type_16px.png" class="vboxDialogTitleIcon" /> ' + trans('Software License Agreement','UIImportLicenseViewer')}); + + $(dlg).html('<p id="vboxImportWizLicTitle" /><textarea rows="20" spellcheck="false" wrap="off" readonly="true"id="vboxImportWizLicContent" style="width:100%; margin:2px; padding:2px;"></textarea>'); + $('#vboxImportWizLicTitle').html(msg.replace('%1',licenses[a]['name'])); + $('#vboxImportWizLicContent').val(licenses[a]['license']); + $(dlg).dialog('open'); + + } else { + vboxImportApp(); + } + + + }; + +} + +/** + * Run the export appliance wizard + */ +function vboxWizardExportApplianceDialog() { + + // reference + var self = this; + + // Extend vboxWizard class + this.parentClass = vboxWizard; + this.parentClass(); + + /* Common settings */ + this.name = 'wizardExportAppliance'; + this.title = trans('Export Virtual Appliance','UIWizardExportApp'); + this.bg = 'images/vbox/vmw_ovf_export_bg.png'; + this.icon = 'export'; + this.steps = 4; + this.height = 500; + this.context = 'UIWizardExportApp'; + this.finishText = trans('Export','UIWizardExportApp'); + + /* Add restore defaults button to last step */ + this.stepButtons = [ + { + 'name' : trans('Restore Defaults','UIWizardExportApp'), + 'steps' : [-1], + 'click' : function(e) { + // Remove export VM properties + $('#vboxExportProps').children().remove(); + // Re-trigger adding them + if(self.mode == 'advanced') + vboxWizardExportApplianceUpdateList(); + else + $('#wizardExportApplianceStep4').trigger('show',e,self); + } + } + ]; + + /* Function run when wizard completes */ + this.onFinish = function() { + + + // Actually export appliances + function vboxExportApp(force) { + + // Each VM + var vmid = null; + var vms = {}; + var vmsAndProps = $('#vboxExportProps').children('tr'); + for(var a = 0; a < vmsAndProps.length; a++) { + if($(vmsAndProps[a]).hasClass('vboxTableParent')) { + vmid = $(vmsAndProps[a]).data('vm').id; + vms[vmid] = {}; + vms[vmid]['id'] = vmid; + continue; + } + + var prop = $(vmsAndProps[a]).data('vmprop'); + vms[vmid][prop] = $(vmsAndProps[a]).children('td:eq(1)').children().first().text(); + + } + + var file = $(self.form).find('[name=wizardExportApplianceLocation]').val(); + var format = $(self.form).find('[name=wizardExportApplianceFormat]').val(); + var manifest = $(self.form).find('[name=wizardExportApplianceManifest]').prop('checked'); + var overwrite = force; + + var l = new vboxLoader(); + l.add('applianceExport',function(d){ + if(d.responseData.progress) { + vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(){ + self.completed.resolve(); + },'progress_export_90px.png', + trans('Export one or more VirtualBox virtual machines as an appliance','UIActionPool').replace(/\.+$/g,''), + vboxBasename(file)); + } else { + self.completed.reject(); + } + },{'format':format,'file':file,'vms':vms,'manifest':manifest,'overwrite':overwrite}); + l.run(); + + $(self.dialog).empty().remove(); + + + } + + /* Remove required classes */ + $(self.form).find('[name=wizardExportApplianceLocation]').removeClass('vboxRequired'); + $('#vboxExportAppVMListContainer').removeClass('vboxRequired'); + + /* Some vms must be selected */ + if($('#vboxExportProps').children('tr').length == 0) { + $('#vboxExportAppVMListContainer').addClass('vboxRequired'); + return; + } + + /* Check to see if file exists */ + var loc = $(self.form).find('[name=wizardExportApplianceLocation]').val(); + if(!loc) { + $(self.form).find('[name=wizardExportApplianceLocation]').addClass('vboxRequired'); + return; + } + + var fileExists = false; + var fe = new vboxLoader(); + fe.add('fileExists',function(d){ + fileExists = d.responseData; + },{'file':loc}); + fe.onLoad = function() { + if(fileExists) { + var buttons = {}; + buttons[trans('Yes','QIMessageBox')] = function() { + vboxExportApp(1); + $(this).empty().remove(); + }; + vboxConfirm(trans('A file named <b>%1</b> already exists. Are you sure you want to replace it?<br /><br />Replacing it will overwrite its contents.','UIMessageCenter').replace('%1',vboxBasename(loc)),buttons,trans('No','QIMessageBox')); + return; + } + vboxExportApp(0); + + }; + fe.run(); + + }; + +} + +/** + * Show the medium encryption dialog + * + * @param {String} context - used in dialog name + * @param {Array} encIds - encryption ids + * + */ +function vboxMediumEncryptionPasswordsDialog(context, encIds, validIds) { + + if(!(encIds && encIds.length)) { return []; } + + var results = $.Deferred(); + + var dialogTitle = trans("%1 - Disk Encryption").replace('%1', context); + + var l = new vboxLoader(); + l.addFileToDOM("panes/mediumEncryptionPasswords.html"); + l.onLoad = function() { + + for(var i = 0; i < encIds.length; i++) { + vboxMediumEncryptionPasswordAdd(encIds[i].id, validIds && validIds.length && jQuery.inArray(encIds[i].id, validIds) > -1); + } + + var buttons = {}; + buttons[trans('OK','QIMessageBox')] = function(){ + // Get passwords + var pws = vboxMediumEncryptionPasswordsGet(); + if(pws === false) + return; + + $(this).trigger('close').empty().remove(); + + results.resolve(pws); + }; + buttons[trans('Cancel','QIMessageBox')] = function(){ + results.reject(); + $(this).trigger('close').empty().remove(); + }; + + $('#vboxMediumEncryptionPasswords').dialog({'closeOnEscape':true,'width':600,'height':400,'buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent','title':'<img src="images/vbox/nw_16px.png" class="vboxDialogTitleIcon" /> ' + dialogTitle}).on("dialogbeforeclose",function(){ + $(this).parent().find('span:contains("'+trans('Cancel','QIMessageBox')+'")').trigger('click'); + }); + + }; + + l.run() + + return results.promise(); +} + +/** + * Show the port forwarding configuration dialog + * @param {Array} rules - list of port forwarding rules to process + * @returns {Object} deferred promise + */ +function vboxPortForwardConfigDialog(rules) { + + var results = $.Deferred(); + + var l = new vboxLoader(); + l.addFileToDOM("panes/settingsPortForwarding.html"); + l.onLoad = function(){ + + vboxSettingsPortForwardingInit(rules); + + var resizeTable = function() { + var h = $('#vboxSettingsPortForwarding').children('table').hide().parent().innerHeight() - 16; + $('#vboxSettingsPortForwarding').children('table').css({'height':h+'px'}).show(); + $('#vboxSettingsPortForwardingListDiv').css({'height':(h-6)+'px','overflow':'auto'}); + + + }; + + var buttons = {}; + buttons[trans('OK','QIMessageBox')] = function(){ + // Get rules + var rules = $('#vboxSettingsPortForwardingList').children('tr'); + var rulesToPass = new Array(); + for(var i = 0; i < rules.length; i++) { + if($(rules[i]).data('vboxRule')[3] == 0 || $(rules[i]).data('vboxRule')[5] == 0) { + vboxAlert(trans("The current port forwarding rules are not valid. " + + "None of the host or guest port values may be set to zero.",'UIMessageCenter')); + return; + } + rulesToPass[i] = $(rules[i]).data('vboxRule'); + } + $(this).trigger('close').empty().remove(); + + results.resolve(rulesToPass); + }; + buttons[trans('Cancel','QIMessageBox')] = function(){ + results.reject(); + $(this).trigger('close').empty().remove(); + }; + + $('#vboxSettingsPortForwarding').dialog({'closeOnEscape':true,'width':600,'height':400,'buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent','title':'<img src="images/vbox/nw_16px.png" class="vboxDialogTitleIcon" /> ' + trans('Port Forwarding Rules','UIMachineSettingsPortForwardingDlg')}).on("dialogbeforeclose",function(){ + $(this).parent().find('span:contains("'+trans('Cancel','QIMessageBox')+'")').trigger('click'); + }).on('dialogresizestop',resizeTable); + + resizeTable(); + }; + l.run(); + + return results.promise(); +} + +/** + * Run the New Virtual Machine Wizard + * @param {String} vmgroup - VM group to add machine to + */ +function vboxWizardNewVMDialog(vmgroup) { + + // reference to self + var self = this; + + // Extend vboxWizard class + this.parentClass = vboxWizard; + this.parentClass(); + + /* Common settings */ + this.name = 'wizardNewVM'; + this.bg = 'images/vbox/vmw_new_welcome_bg.png'; + this.title = trans('Create Virtual Machine','UIWizardNewVM'); + this.icon = 'vm_new'; + this.steps = 3; + this.vmgroup = vmgroup; + this.context = 'UIWizardNewVM'; + this.finishText = trans('Create','UIWizardNewVM'); + this.data = [ + {'fn':'vboxGetMedia','callback':function(d){$('#vboxPane').data('vboxMedia',d.responseData);}} + ]; + + /* Function to run when wizard completes */ + this.onFinish = function() { + + // Callback to finish wizard + var vboxNewVMFinish = function() { + + // Get parameters + var disk = ''; + if($(self.form).find('[name=newVMDisk]:checked').val() == 'existing') { + disk = $(self.form)[0].newVMDiskSelect.options[$(self.form)[0].newVMDiskSelect.selectedIndex].value; + disk = vboxMedia.getMediumById(disk).location; + } + var name = jQuery.trim($(self.form).find('[name=newVMName]').val()); + var ostype = $(self.form).find('[name=newVMOSType]').val(); + var mem = parseInt($(self.form).find('[name=wizardNewVMSizeValue]').val()); + + // Show loading screen + var l = new vboxLoader('machineCreate'); + l.showLoading(); + + $.when(vboxAjaxRequest('machineCreate',{'disk':disk,'ostype':ostype,'memory':mem,'name':name,'group':vmgroup})) + .always(function() { + l.removeLoading(); + }).done(function(res){ + + if(res.responseData.exists) { + vboxAlert(trans('<p>Cannot create the machine folder <b>%1</b> in the parent folder <nobr><b>%2</b>.</nobr></p><p>This folder already exists and possibly belongs to another machine.</p>','UIMessageCenter').replace('%1',vboxBasename(res.exists)).replace('%2',vboxDirname(res.exists))); + + } else if(res.success) { + $(self.dialog).empty().remove(); + var lm = new vboxLoader('machineCreate'); + lm.add('vboxGetMedia',function(d){$('#vboxPane').data('vboxMedia',d.responseData);}); + lm.onLoad = function() { + self.completed.resolve(); + }; + lm.run(); + } else { + self.completed.reject(); + } + + }); + + }; + + // Name must exist + if(!jQuery.trim($(self.form).find('[name=newVMName]').val())) { + $(self.form).find('[name=newVMName]').addClass('vboxRequired'); + return; + } + $(self.form).find('[name=newVMName]').removeClass('vboxRequired'); + + // Make sure file does not exist + var fnl = new vboxLoader(); + fnl.add('vboxGetComposedMachineFilename',function(d){ + + loc = vboxDirname(d.responseData); + var fileExists = false; + + var fe = new vboxLoader('fileExists'); + fe.add('fileExists',function(d){ + fileExists = d.responseData; + },{'file':loc}); + fe.onLoad = function() { + + if(fileExists) { + + vboxAlert(trans('<p>Cannot create the machine folder <b>%1</b> in the parent folder <nobr><b>%2</b>.</nobr></p><p>This folder already exists and possibly belongs to another machine.</p>','UIMessageCenter').replace('%1',vboxBasename(loc)).replace('%2',vboxDirname(loc))); + // Go back + self.displayStep(1); + + return; + } + + // Start new harddisk wizard if create new is selected + if($(self.form).find('[name=newVMDisk]:checked').val() == 'create') { + + // Recommended size + var size = newVMOSTypesObj[$(self.form).find('[name=newVMOSType]').val()].recommendedHDD; + + $.when(new vboxWizardNewHDDialog({'name':jQuery.trim($(self.form).find('[name=newVMName]').val()),'size':size,'group':vmgroup}).run()) + .done(function(med){ + + $(self.form).find('[name=newVMDisk]').eq(2).trigger('click').prop('checked',true); + + // Add newly created disk as option and select it + vmNewFillExistingDisks(med); + + vboxNewVMFinish(); + + }); + + return; + + } else if($(self.form).find('[name=newVMDisk]:checked').val() == 'none') { + + buttons = {}; + buttons[trans('Continue','UIMessageCenter')] = function(){ + $(this).empty().remove(); + vboxNewVMFinish(); + }; + vboxConfirm(trans('You are about to create a new virtual machine without a hard disk. You will not be able to install an operating system on the machine until you add one. In the mean time you will only be able to start the machine using a virtual optical disk or from the network.','UIMessageCenter'), buttons, trans('Go Back','UIMessageCenter')); + return; + } + + vboxNewVMFinish(); + + }; + fe.run(); + + + },{'name':document.forms['frmwizardNewVM'].newVMName.value, 'group':vmgroup}); + + fnl.run(); + }; + +} + +/** + * Run the Clone Virtual Machine Wizard + * @param {Object} args - wizard data - args.vm and args.snapshot should be populated + * @returns {Object} deferred promise + * @see vboxWizard() + */ +function vboxWizardCloneVMDialog(args) { + + // self reference + var self = this; + + // Extend vboxWizard class + this.parentClass = vboxWizard; + this.parentClass(); + + /* Common options */ + this.name = 'wizardCloneVM'; + this.title = trans('Clone Virtual Machine','UIWizardCloneVM'); + this.bg = 'images/vbox/vmw_clone_bg.png'; + this.icon = 'vm_clone'; + this.finishText = trans('Clone','UIWizardCloneVM'); + this.context = 'UIWizardCloneVM'; + this.widthAdvanced = 450; + this.heightAdvanced = 350; + + /* Override run() because we need VM data */ + this.parentRun = this.run; + this.run = function() { + + var l = new vboxLoader('vboxWizardCloneVMInit'); + l.showLoading(); + $.when(vboxVMDataMediator.getVMDetails(args.vm.id)).always(function() { + // Always remove loading screen + l.removeLoading(); + }).done(function(d){ + self.steps = (d.snapshotCount > 0 ? 3 : 2); + self.args = $.extend(true,args,{'vm':d}); + self.parentRun(); + }); + + return self.completed.promise(); + + }; + + /* Function to run when finished */ + this.onFinish = function() { + + // Get parameters + var name = jQuery.trim($(self.form).find('[name=machineCloneName]').val()); + var src = self.args.vm.id; + var snapshot = self.args.snapshot; + var allNetcards = $(self.form).find('[name=vboxCloneReinitNetwork]').prop('checked'); + + if(!name) { + $(self.form).find('[name=machineCloneName]').addClass('vboxRequired'); + return; + } + + + // Only two steps? We can assume that state has no child states. + // Force current state only + var vmState = 'MachineState'; + if(self.steps > 2 || self.mode == 'advanced') { + vmState = $(self.form).find('[name=vmState]:checked').val(); + } + + // Full / linked clone + var cLink = $(self.form).find('[name=vboxCloneType]').eq(1).prop('checked'); + + // wrap function + var vbClone = function(sn) { + + $.when(vboxAjaxRequest('machineClone', {'name':name,'vmState':vmState,'src':src,'snapshot':sn,'reinitNetwork':allNetcards,'link':cLink})) + .done(function(d){ + if(d.responseData.progress) { + var registerVM = d.responseData.settingsFilePath; + vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(ret) { + $.when(vboxAjaxRequest('machineAdd',{'file':registerVM})).done(function(){ + + $.when(vboxAjaxRequest('vboxGetMedia')).done(function(dat){ + $('#vboxPane').data('vboxMedia',dat.responseData); + self.completed.resolve(); + }).fail(function(){ + self.completed.reject(); + }); + }).fail(function(){ + self.completed.reject(); + }); + },'progress_clone_90px.png',trans('Clone selected virtual machine','UIActionPool'), + self.args.vm.name + ' > ' + name); + } else { + self.completed.reject(); + } + }).fail(function(){ + self.completed.reject(); + }); + }; + + // Check for linked clone, but not a snapshot + if(cLink && !self.args.snapshot) { + + $.when(vboxAjaxRequest('snapshotTake', + {'vm':src,'name':trans('Linked Base for %1 and %2','UIWizardCloneVM').replace('%1',self.args.vm.name).replace('%2',name),'description':''})) + .done(function(d){ + + if(d.responseData.progress) { + + vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(){ + $.when(vboxVMDataMediator.getVMDetails(src, true)).done(function(md){ + vbClone(md.currentSnapshot); + }); + }, + 'progress_snapshot_create_90px.png', + trans('Take a snapshot of the current virtual machine state','UIActionPool'), + self.args.vm.name); + + } else if(d && d.error) { + self.completed.reject(); + vboxAlert(d.error); + } + + }).fail(function(){ + self.completed.reject(); + }); + + // Just clone + } else { + vbClone(snapshot); + } + + $(self.dialog).empty().remove(); + + }; +} + +/** + * Run the VM Log Viewer dialog + * @param {String} vm - uuid or name of virtual machine to obtain logs for + */ +function vboxShowLogsDialogInit(vm) { + + $('#vboxPane').append($('<div />').attr({'id':'vboxVMLogsDialog'})); + + var l = new vboxLoader(); + l.add('machineGetLogFilesList',function(r){ + $('#vboxVMLogsDialog').data({'logs':r.responseData.logs,'logpath':r.responseData.path}); + },{'vm':vm.id}); + l.addFileToDOM('panes/vmlogs.html',$('#vboxVMLogsDialog')); + l.onLoad = function(){ + var buttons = {}; + buttons[trans('Refresh','UIVMLogViewer')] = function() { + l = new vboxLoader(); + l.add('machineGetLogFilesList',function(r){ + $('#vboxVMLogsDialog').data({'logs':r.responseData.logs,'logpath':r.responseData.path}); + + },{'vm':vm.id}); + l.onLoad = function(){ + vboxShowLogsInit(vm); + }; + l.run(); + }; + buttons[trans('Close','UIVMLogViewer')] = function(){$(this).trigger('close').empty().remove();}; + $('#vboxVMLogsDialog').dialog({'closeOnEscape':true,'width':800,'height':500,'buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent','title':'<img src="images/vbox/vm_show_logs_16px.png" class="vboxDialogTitleIcon" /> '+ trans('%1 - VirtualBox Log Viewer','UIVMLogViewer').replace('%1',vm.name)}).on("dialogbeforeclose",function(){ + $(this).parent().find('span:contains("'+trans('Close','UIVMLogViewer')+'")').trigger('click'); + }); + vboxShowLogsInit(vm); + }; + l.run(); + +} + +/** + * Show the Virtual Media Manager Dialog + * @param {Boolean} select - true to display "Select" button and resolve with selected medium + * @param {String} type - optionally restrict media to media of this type + * @param {Boolean} hideDiff - optionally hide differencing HardDisk media + * @param {String} mPath - optional path to use when adding or creating media through the VMM dialog + * @returns {Object} deferred promise + */ +function vboxVMMDialog(select,type,hideDiff,mPath) { + + var results = $.Deferred(); + + $('#vboxPane').append($('<div />').attr({'id':'vboxVMMDialog','class':'vboxVMMDialog'})); + + var l = new vboxLoader(); + l.add('getConfig',function(d){$('#vboxPane').data('vboxConfig',d.responseData);}); + l.add('vboxSystemPropertiesGet',function(d){$('#vboxPane').data('vboxSystemProperties',d.responseData);}); + l.add('vboxGetMedia',function(d){$('#vboxPane').data('vboxMedia',d.responseData);}); + l.addFileToDOM('panes/vmm.html',$('#vboxVMMDialog')); + l.onLoad = function() { + var buttons = {}; + if(select) { + buttons[trans('Select','UIMediumManager')] = function() { + var sel = null; + switch($("#vboxVMMTabs").tabs('option','active')) { + case 0: /* HardDisks */ + sel = $('#vboxVMMHDList').find('tr.vboxListItemSelected').first(); + break; + case 1: /* DVD */ + sel = $('#vboxVMMCDList').find('tr.vboxListItemSelected').first(); + break; + default: + sel = $('#vboxVMMFDList').find('tr.vboxListItemSelected').first(); + } + if($(sel).length) { + vboxMedia.updateRecent(vboxMedia.getMediumById($(sel).data('medium'))); + results.resolve($(sel).data('medium')); + } + $('#vboxVMMDialog').trigger('close').empty().remove(); + }; + } + buttons[trans('Close','UIMessageCenter')] = function() { + $('#vboxVMMDialog').trigger('close').empty().remove(); + results.reject(); + }; + + $("#vboxVMMDialog").dialog({'closeOnEscape':true,'width':800,'height':500,'buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent vboxVMMDialog','title':'<img src="images/vbox/diskimage_16px.png" class="vboxDialogTitleIcon" /> '+trans('Virtual Media Manager','VBoxMediaManagerDlg')}).on("dialogbeforeclose",function(){ + $(this).parent().find('span:contains("'+trans('Close','UIMessageCenter')+'")').trigger('click'); + }); + + vboxVMMInit(hideDiff,mPath); + + if(type) { + switch(type) { + case 'HardDisk': + $("#vboxVMMTabs").tabs('option','active',0); + $("#vboxVMMTabs").tabs('disable',1); + $("#vboxVMMTabs").tabs('disable',2); + break; + case 'DVD': + $("#vboxVMMTabs").tabs('option','active',1); + $("#vboxVMMTabs").tabs('disable',0); + $("#vboxVMMTabs").tabs('disable',2); + break; + case 'Floppy': + $("#vboxVMMTabs").tabs('option','active',2); + $("#vboxVMMTabs").tabs('disable',0); + $("#vboxVMMTabs").tabs('disable',1); + break; + default: + $("#vboxVMMTabs").tabs('option','active',0); + break; + } + } + }; + l.run(); + + return results.promise(); +} + +/** + * Run the New Virtual Disk wizard + * @param {Object} suggested - sugggested defaults such as hard disk name and path + */ +function vboxWizardNewHDDialog(suggested) { + + // reference + var self = this; + + // Extend vboxWizard class + this.parentClass = vboxWizard; + this.parentClass(); + + /* Common options */ + this.name = 'wizardNewHD'; + this.title = trans('Create Virtual Hard Disk','UIWizardNewVD'); + this.bg = 'images/vbox/vmw_new_harddisk_bg.png'; + this.icon = 'hd'; + this.steps = 3; + this.suggested = suggested; + this.context = 'UIWizardNewVD'; + this.finishText = trans('Create','UIWizardNewVD'); + this.height = 450; + + this.data = [ + {'fn':'vboxSystemPropertiesGet','callback':function(d){$('#vboxPane').data('vboxSystemProperties',d.responseData);}}, + {'fn':'vboxGetMedia','callback':function(d){$('#vboxPane').data('vboxMedia',d.responseData);}} + ]; + + // Compose folder if suggested name exists + if(this.suggested && this.suggested.name) { + if(!this.suggested['group']) this.suggested.group = ''; + this.data.push( + {'fn':'vboxGetComposedMachineFilename','callback':function(d){ + self.suggested.path = vboxDirname(d.responseData)+$('#vboxPane').data('vboxConfig').DSEP; + },'args':{'name':this.suggested.name, 'group':this.suggested.group}}); + } + + /* Function to run when wizard completes */ + this.onFinish = function() { + + // Fix size if we need to + var mbytes = vboxConvertMbytes($(self.form).find('[name=wizardNewHDSizeValue]').val()); + $(self.form).find('[name=wizardNewHDSizeValue]').val(vboxMbytesConvert(mbytes)); + $('#wizardNewHDSizeLabel').html(vboxMbytesConvert(mbytes) + ' ('+mbytes+' '+trans('MB','VBoxGlobal')+')'); + + // Determine file location + var file = $(self.form).find('[name=wizardNewHDLocation]').val(); + if(file.search(/[\/|\\]/) < 0) { + // just a name + if(self.suggested.path) { + if($('#vboxPane').data('vboxConfig').enforceVMOwnership==true){ + file = self.suggested.path + $('#vboxPane').data('vboxConfig').DSEP + $('#vboxPane').data('vboxSession').user + "_" + file; + } else { + file = self.suggested.path + $('#vboxPane').data('vboxConfig').DSEP + file; + } + } else{ + if($('#vboxPane').data('vboxConfig').enforceVMOwnership==true){ + file = $('#vboxPane').data('vboxSystemProperties').homeFolder + $('#vboxPane').data('vboxConfig').DSEP + $('#vboxPane').data('vboxSession').user + "_" + file; + } else { + file = $('#vboxPane').data('vboxSystemProperties').homeFolder + $('#vboxPane').data('vboxConfig').DSEP + file; + } + } + + // Enforce VM ownership + } else if($('#vboxPane').data('vboxConfig').enforceVMOwnership==true) { + // has user ownership so use folderbased + var nameIndex = file.lastIndexOf($('#vboxPane').data('vboxConfig').DSEP); + var path = file.substr(0,nameIndex); + var name = file.substr(nameIndex+1,file.length); + file = path +$('#vboxPane').data('vboxConfig').DSEP + $('#vboxPane').data('vboxSession').user + "_" + name; + } + + var format = $(self.form)[0].elements['newHardDiskFileType']; + var formatOpts = {}; + for(var i = 0; i < format.length; i++) { + if(format[i].checked) { + formatOpts = $(format[i]).closest('tr').data('vboxFormat'); + format=format[i].value; + break; + } + } + + // append filename ext? + if(jQuery.inArray(file.substring(file.lastIndexOf('.')+1).toLowerCase(),formatOpts.extensions) < 0) { + file += '.'+formatOpts.extensions[0]; + } + + // Normalize file + file = file.replace($('#vboxPane').data('vboxConfig').DSEP+$('#vboxPane').data('vboxConfig').DSEP,$('#vboxPane').data('vboxConfig').DSEP); + + /* Check to see if file exists */ + var fileExists = false; + var l = new vboxLoader('fileExists'); + l.add('fileExists',function(d){ + fileExists = d.responseData; + },{'file':file}); + l.onLoad = function() { + if(fileExists) { + vboxAlert(trans("<p>The hard disk storage unit at location <b>%1</b> already " + + "exists. You cannot create a new virtual hard disk that uses this " + + "location because it can be already used by another virtual hard " + + "disk.</p>" + + "<p>Please specify a different location.</p>",'UIMessageCenter').replace('%1',file)); + return; + } + var fsplit = $(self.form).find('[name=newHardDiskSplit]').prop('checked'); + var size = vboxConvertMbytes($(self.form).find('[name=wizardNewHDSizeValue]').val()); + var type = ($(self.form).find('[name=newHardDiskType]').eq(1).prop('checked') ? 'fixed' : 'dynamic'); + var nl = new vboxLoader('mediumCreateBaseStorage'); + nl.add('mediumCreateBaseStorage',function(d){ + if(d.responseData.progress) { + vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(ret) { + var ml = new vboxLoader(); + ml.add('vboxGetMedia',function(dat){$('#vboxPane').data('vboxMedia',dat.responseData);}); + ml.onLoad = function() { + var med = vboxMedia.getMediumByLocation(file); + if(med) { + vboxMedia.updateRecent(med); + self.completed.resolve(med.id); + } else { + self.completed.reject(); + } + }; + ml.run(); + },'progress_media_create_90px.png',trans('Create a virtual hard disk now','UIWizardNewVM'), + vboxBasename(file),true); + } else { + self.completed.reject(); + } + },{'file':file,'type':type,'size':size,'format':format,'split':fsplit}); + nl.run(); + + $(self.dialog).empty().remove(); + }; + l.run(); + + }; +} + +/** + * Run the Copy Virtual Disk wizard + * @param {Object} suggested - sugggested defaults such as hard disk name and path + */ +function vboxWizardCopyHDDialog(suggested) { + + // reference + var self = this; + + /* Extend vboxWizard class */ + this.parentClass = vboxWizard; + this.parentClass(); + + /* Common options */ + this.name = 'wizardCopyHD'; + this.title = trans('Copy Virtual Hard Disk','UIWizardCloneVD'); + this.bg = 'images/vbox/vmw_new_harddisk_bg.png'; + this.icon = 'hd'; + this.steps = 4; + this.suggested = suggested; + this.context = 'UIWizardCloneVD'; + this.finishText = trans('Copy','UIWizardCloneVD'); + this.height = 450; + + this.data = [ + {'fn':'vboxSystemPropertiesGet','callback':function(d){$('#vboxPane').data('vboxSystemProperties',d.responseData);}}, + {'fn':'vboxGetMedia','callback':function(d){$('#vboxPane').data('vboxMedia',d.responseData);}} + ]; + + + /* Function run when wizard completes */ + this.onFinish = function() { + + var format = $(self.form)[0].elements['copyHDFileType']; + var formatOpts = {}; + for(var i = 0; i < format.length; i++) { + if(format[i].checked) { + formatOpts = $(format[i]).closest('tr').data('vboxFormat'); + break; + } + } + + var src = $(self.form).find('[name=copyHDDiskSelect]').val(); + var type = ($(self.form).find('[name=newHardDiskType]').eq(1).prop('checked') ? 'fixed' : 'dynamic'); + var format = $(self.form)[0].elements['copyHDFileType']; + for(var i = 0; i < format.length; i++) { + if(format[i].checked) { + format=format[i].value; + break; + } + } + + var fsplit = $(self.form).find('[name=newHardDiskSplit]').prop('checked') && vboxMedia.formatSupportsSplit(format); + + var loc = jQuery.trim($(self.form).find('[name=wizardCopyHDLocation]').val()); + if(!loc) { + $(self.form).find('[name=wizardCopyHDLocation]').addClass('vboxRequired'); + return; + } + $(self.form).find('[name=wizardCopyHDLocation]').removeClass('vboxRequired'); + if(loc.search(/[\/|\\]/) < 0) { + if($('#wizardCopyHDStep4').data('suggestedpath')) { + loc = $('#wizardCopyHDStep4').data('suggestedpath') + loc; + } else { + loc = vboxDirname(vboxMedia.getMediumById(src).location) + $('#vboxPane').data('vboxConfig').DSEP + loc; + } + } + + // append ext? + if(jQuery.inArray(loc.substring(loc.lastIndexOf('.')+1).toLowerCase(),formatOpts.extensions) < 0) { + loc += '.'+formatOpts.extensions[0]; + } + + + /* Check to see if file exists */ + var fileExists = false; + var fe = new vboxLoader(); + fe.add('fileExists',function(d){ + fileExists = d.responseData; + },{'file':loc}); + fe.onLoad = function() { + if(fileExists) { + vboxAlert(trans("<p>The hard disk storage unit at location <b>%1</b> already " + + "exists. You cannot create a new virtual hard disk that uses this " + + "location because it can be already used by another virtual hard " + + "disk.</p>" + + "<p>Please specify a different location.</p>",'UIMessageCenter').replace('%1',loc)); + return; + } + $(self.dialog).empty().remove(); + + var l = new vboxLoader(); + l.add('mediumCloneTo',function(d){ + if(d.responseData.progress) { + vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(ret,mid) { + var ml = new vboxLoader(); + ml.add('vboxGetMedia',function(dat){$('#vboxPane').data('vboxMedia',dat.responseData);}); + ml.onLoad = function() { + med = vboxMedia.getMediumByLocation(loc); + vboxMedia.updateRecent(med); + self.completed.resolve(mid); + }; + ml.run(); + },'progress_media_create_90px.png',trans('Copy Virtual Hard Disk','UIWizardCloneVD'), + vboxBasename(vboxMedia.getMediumById(src).location) + ' > ' + vboxBasename(loc)); + } else { + self.completed.reject(); + } + },{'src':vboxMedia.getMediumById(src).location,'type':type,'format':format,'location':loc,'split':fsplit}); + l.run(); + }; + fe.run(); + + + }; +} + +/** + * Display guest network adapters dialog + * @param {String} vm - virtual machine uuid or name + */ +function vboxGuestNetworkAdaptersDialogInit(vm) { + + /* + * Dialog + */ + $('#vboxPane').append($('<div />').attr({'id':'vboxGuestNetworkDialog','style':'display: none'})); + + /* + * Loader + */ + var l = new vboxLoader(); + l.addFileToDOM('panes/guestNetAdapters.html',$('#vboxGuestNetworkDialog')); + l.onLoad = function(){ + + var buttons = {}; + buttons[trans('Close','UIVMLogViewer')] = function() {$('#vboxGuestNetworkDialog').trigger('close').empty().remove();}; + $('#vboxGuestNetworkDialog').dialog({'closeOnEscape':true,'width':500,'height':250,'buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxDialogContent','title':'<img src="images/vbox/nw_16px.png" class="vboxDialogTitleIcon" /> ' + trans('Guest Network Adapters','VBoxGlobal')}).on("dialogbeforeclose",function(){ + $(this).parent().find('span:contains("'+trans('Close','UIVMLogViewer')+'")').trigger('click'); + }); + + // defined in pane + vboxVMNetAdaptersInit(vm,nic); + }; + l.run(); + +} + + +/** + * Display Global Preferences dialog + */ + +function vboxGlobalPrefsDialog() { + + // Prefs + var panes = new Array( + {'name':'GlobalGeneral','label':'General','icon':'machine','context':'UIGlobalSettingsGeneral'}, + {'name':'GlobalLanguage','label':'Language','icon':'site','context':'UIGlobalSettingsLanguage'}, + {'name':'GlobalNetwork','label':'Network','icon':'nw','context':'UIGlobalSettingsNetwork','tabbed':true}, + {'name':'GlobalUsers','label':'Users','icon':'register','context':'UIUsers'} + ); + + var data = new Array( + {'fn':'hostOnlyInterfacesGet','callback':function(d){$('#vboxSettingsDialog').data('vboxHostOnlyInterfaces',d.responseData);}}, + {'fn':'vboxSystemPropertiesGet','callback':function(d){$('#vboxSettingsDialog').data('vboxSystemProperties',d.responseData);}}, + {'fn':'vboxNATNetworksGet','callback':function(d){$('#vboxSettingsDialog').data('vboxNATNetworks',d.responseData);}}, + {'fn':'getUsers','callback':function(d){$('#vboxSettingsDialog').data('vboxUsers',d.responseData);}} + ); + + // Check for noAuth setting + if($('#vboxPane').data('vboxConfig').noAuth || !$('#vboxPane').data('vboxSession').admin || !$('#vboxPane').data('vboxConfig').authCapabilities.canModifyUsers) { + panes.pop(); + data.pop(); + } + + $.when(vboxSettingsDialog(trans('Preferences...','UIActionPool').replace(/\./g,''), + panes,data,null,'global_settings','UISettingsDialogGlobal')) + .done(function(){ + + var l = new vboxLoader(); + + // Language change? + if($('#vboxSettingsDialog').data('language') && $('#vboxSettingsDialog').data('language') != __vboxLangName) { + vboxSetCookie('vboxLanguage',$('#vboxSettingsDialog').data('language')); + l.onLoad = function(){location.reload(true);}; + + } + + l.add('vboxNATNetworksSave',function(){return;},{'networks':$('#vboxSettingsDialog').data('vboxNATNetworks')}); + l.add('hostOnlyInterfacesSave',function(){return;},{'networkInterfaces':$('#vboxSettingsDialog').data('vboxHostOnlyInterfaces').networkInterfaces}); + l.add('vboxSystemPropertiesSave',function(){return;},{'SystemProperties':$('#vboxSettingsDialog').data('vboxSystemProperties')}); + l.run(); + + // Update system properties + $('#vboxPane').data('vboxSystemProperties',$('#vboxSettingsDialog').data('vboxSystemProperties')); + + }); + +} + + + +/** + * Display a virtual machine settings dialog + * @param {String} vm - uuid or name of virtual machine + * @param {String} pane - optionally automatically select pane when dialog is displayed + * @returns {Object} deferred promise + */ +function vboxVMsettingsDialog(vm,pane) { + + var results = $.Deferred(); + + if(typeof(vm) == 'string') + vm = vboxVMDataMediator.getVMData(vm); + + // Only show these dialogs once per change + var reloadConfirmShowing = false; + + // Handler for when VM settings have changed + ///////////////////////////////////////////// + var machineSettingsChanged = function(e, eventList) { + + for(var i = 0; i < eventList.length; i++) { + + //////////////////////////////// + // + // Machine data changed.. + // + //////////////////////////////// + switch(eventList[i].eventType) { + + case 'OnMachineStateChanged': + + if(!eventList[i].machineId || eventList[i].machineId != vm.id) break; + + // Display loading screen + var l = new vboxLoader(); + l.showLoading(); + + $.when(vboxVMDataMediator.getVMDataCombined(vm.id)).done(function(vmData) { + // data received from deferred object + $('#vboxSettingsDialog').data('vboxMachineData',vmData); + $('#vboxSettingsDialog').data('vboxFullEdit', (vboxVMStates.isPoweredOff(vmData) && !vboxVMStates.isSaved(vmData))); + $('#vboxSettingsDialog').trigger('dataLoaded'); + l.removeLoading(); + if(vboxVMStates.isRunning(vmData)) { + vboxAlert(trans('The virtual machine that you are changing has been started. Only certain settings can be changed while a machine is running. All other changes will be lost if you close this window now.','UIMessageCenter')); + } + }); + + break; + + // Unregistered machine + case 'OnMachineRegistered': + + if(!eventList[i].machineId || eventList[i].machineId != vm.id || eventList[i].registered) break; + + $('#vboxSettingsDialog').parent().find('span:contains("'+trans('Cancel','QIMessageBox')+'")').trigger('click'); + break; + + case 'OnMachineDataChanged': + case 'OnNetworkAdapterChanged': + case 'OnVRDEServerInfoChanged': + case 'OnCPUChanged': + case 'OnStorageControllerChanged': + case 'OnMediumChanged': + case 'OnVRDEServerChanged': + case 'OnUSBControllerChanged': + case 'OnSharedFolderChanged': + case 'OnCPUExecutionCapChanged': + case 'OnStorageDeviceChanged': + case 'OnNATRedirect': + + if(!eventList[i].machineId || eventList[i].machineId != vm.id) break; + + // already showing reload confirmation + if(reloadConfirmShowing) break; + + var buttons = {}; + buttons[trans('Reload settings','UIMessageCenter')] = function() { + + // Display loading screen + var l = new vboxLoader(); + l.showLoading(); + + $(this).empty().remove(); + + /* + * Data to be reloaded + */ + var reload = [ + vboxAjaxRequest('vboxGetMedia',{}).done(function(d){$('#vboxPane').data('vboxMedia',d.responseData);}), + + vboxAjaxRequest('getNetworking',{}).done(function(d){$('#vboxSettingsDialog').data('vboxNetworking',d.responseData);}), + + vboxAjaxRequest('vboxRecentMediaGet',{}).done(function(d){$('#vboxPane').data('vboxRecentMedia',d.responseData);}), + + vboxAjaxRequest('consoleGetSharedFolders',{'vm':vm.id}).done(function(d){$('#vboxSettingsDialog').data('vboxTransientSharedFolders',d.responseData);}), + + $.when(vboxVMDataMediator.getVMDataCombined(vm.id)).done(function(vmData) { + + // data received from deferred object + $('#vboxSettingsDialog').data('vboxMachineData',vmData); + $('#vboxSettingsDialog').data('vboxFullEdit', (vboxVMStates.isPoweredOff(vmData) && !vboxVMStates.isSaved(vmData))); + + }) + ]; + + // Only when all of these are done + $.when.apply($, reload).done(function(){ + + /* Change title and tell dialog that data is loaded */ + $('#vboxSettingsDialog').trigger('dataLoaded').dialog('option','title','<img src="images/vbox/vm_settings_16px.png" class="vboxDialogTitleIcon" /> ' + + $('<div />').text($('#vboxSettingsDialog').data('vboxMachineData').name).text() + ' - ' + trans('Settings','UISettingsDialog')); + + l.removeLoading(); + reloadConfirmShowing = false; + }); + + + }; + + reloadConfirmShowing = true; + + vboxConfirm(trans("<p>The machine settings were changed while you were editing them. You currently have unsaved setting changes.</p><p>Would you like to reload the changed settings or to keep your own changes?</p>",'UIMessageCenter'), + buttons, + trans('Keep changes', 'UIMessageCenter'), function(){ + reloadConfirmShowing = false; + }); + + return; + + } + } + }; + + // Watch for changed VM settings + $('#vboxPane').on('vboxEvents',machineSettingsChanged); + + $.when(vboxVMDataMediator.getVMDataCombined(vm.id)).done(function(vmData) { + + + /* + * Settings dialog data + */ + var dataList = new Array( + {'fn':'vboxGetMedia','callback':function(d){ + + $('#vboxPane').data('vboxMedia',d.responseData); + + // data received from deferred object + $('#vboxSettingsDialog').data('vboxMachineData',vmData); + $('#vboxSettingsDialog').data('vboxFullEdit', (vboxVMStates.isPoweredOff(vmData) && !vboxVMStates.isSaved(vmData))); + + }}, + {'fn':'getNetworking','callback':function(d){$('#vboxSettingsDialog').data('vboxNetworking',d.responseData);}}, + {'fn':'hostGetDetails','callback':function(d){$('#vboxSettingsDialog').data('vboxHostDetails',d.responseData);}}, + {'fn':'vboxGetEnumerationMap','callback':function(d){$('#vboxSettingsDialog').data('vboxNetworkAdapterTypes',d.responseData);},'args':{'class':'NetworkAdapterType'}}, + {'fn':'vboxGetEnumerationMap','callback':function(d){$('#vboxSettingsDialog').data('vboxAudioControllerTypes',d.responseData);},'args':{'class':'AudioControllerType'}}, + {'fn':'vboxRecentMediaGet','callback':function(d){$('#vboxPane').data('vboxRecentMedia',d.responseData);}}, + {'fn':'consoleGetSharedFolders','callback':function(d){$('#vboxSettingsDialog').data('vboxTransientSharedFolders',d.responseData);},'args':{'vm':vm.id}} + ); + + /* + * Settings dialog panes + */ + var panes = new Array( + + {name:'General',label:'General',icon:'machine',tabbed:true,context:'UIMachineSettingsGeneral'}, + {name:'System',label:'System',icon:'chipset',tabbed:true,context:'UIMachineSettingsSystem'}, + {name:'Display',label:'Display',icon:'vrdp',tabbed:true,context:'UIMachineSettingsDisplay'}, + {name:'Storage',label:'Storage',icon:'attachment',context:'UIMachineSettingsStorage'}, + {name:'Audio',label:'Audio',icon:'sound',context:'UIMachineSettingsAudio'}, + {name:'Network',label:'Network',icon:'nw',tabbed:true,context:'UIMachineSettingsNetwork'}, + {name:'SerialPorts',label:'Serial Ports',icon:'serial_port',tabbed:true,context:'UIMachineSettingsSerial'}, + {name:'ParallelPorts',label:'Parallel Ports',icon:'parallel_port',tabbed:true,disabled:(!$('#vboxPane').data('vboxConfig').enableLPTConfig),context:'UIMachineSettingsParallel'}, + {name:'USB',label:'USB',icon:'usb',context:'UIMachineSettingsUSB'}, + {name:'SharedFolders',label:'Shared Folders',icon:'sf',context:'UIMachineSettingsSF'} + + ); + + /* + * Check for encryption settings change + */ + var presaveCallback = function() { + + if(!$('#vboxSettingsDialog').data('vboxEncSettingsChanged')) + return true; + + var encMediaSettings = $.Deferred(); + + // Validate + if(!vboxSettingsGeneralValidate()) { + $('#vboxSettingsMenuList').children('li:eq(0)').first().click(); + $('#vboxSettingsPane-General').tabs('option','active', 3); + encMediaSettings.reject(); + return encMediaSettings; + } + + var vm = $('#vboxSettingsDialog').data('vboxMachineData'); + var media = vboxStorage.getAttachedBaseMedia(vm); + var encIds = vboxMedia.getEncryptedMediaIds(media); + var encMedia = vboxMedia.getEncryptedMedia(media); + + var formCipher = $('#vboxSettingsDialog').data('vboxEncCipher'); + var formPassword = $('#vboxSettingsDialog').data('vboxEncPw'); + var formEncEnabled = $('#vboxSettingsDialog').data('vboxEncEnabled'); + + // If encryption is not enabled, cipher needs to be blank + if(!formEncEnabled) { + formCipher = ''; + } + + // Get encryption password(s) + $.when(vboxMediumEncryptionPasswordsDialog(vm.name, encIds)) + + .done(function(pwdata) { + + var runs = [] + + // Each medium attached + for(var i = 0; i < media.length; i++) { + // Only hard disks + if(media[i].deviceType != 'HardDisk') continue; + + var id = vm.name; + var oldpw = ""; + var cipher = null; + + // Check for existing encryption setting + for(var a = 0; a < encMedia.length; a++) { + if(encMedia[a].medium == media[i].id) { + cipher = media[i].encryptionSettings.cipher; + // Look in passwords for id + for(var b = 0; b < pwdata.length; b++) { + if(pwdata[b].id == id) { + oldpw = pwdata[b].password; + break; + } + } + break; + } + } + + runs.push({ + medium: media[i].id, + cipher: cipher, + encId: id, + password: oldpw + }); + + } + // No encrypted media changes + if(!runs.length) { + encMediaSettings.resolve(); + return; + } + + var l = new vboxLoader(); + l.showLoading(); + + (function doruns(encMediaRuns){ + + if(!encMediaRuns.length) { + l.removeLoading(); + encMediaSettings.resolve(); + return; + } + + var run = encMediaRuns.shift(); + + // If encryption is enabled, and cypher is blank / "Leave Unchanged" + // keep the original cipher + var mcipher = formCipher; + if(formEncEnabled && !mcipher) { + mcipher = run.cipher; + } + + var rdata = { + medium: run.medium, + id: run.encId, + old_password: run.password, + cipher: mcipher, + password: formPassword + }; + + $.when(vboxAjaxRequest('mediumChangeEncryption',rdata)).done(function(d){ + + if(d.responseData.progress) { + var icon = 'progress_media_create_90px.png'; + var title = trans('Encryption'); + vboxProgress({'progress':d.responseData.progress,'persist':d.persist},function(){ + // Loop + doruns(encMediaRuns); + },icon,title,vboxMedia.getMediumById(run.medium).name, true); + } else { + l.removeLoading(); + encMediaSettings.reject(); + return; + } + + }); + + })(runs); + + }) + .fail(function() { + encMediaSettings.reject(); + }); + + return encMediaSettings.promise(); + + } + + $.when(vboxSettingsDialog(vmData.name + ' - ' + trans('Settings','UISettingsDialog'),panes,dataList,pane,'vm_settings','UISettingsDialogMachine', presaveCallback)) + + // Always run this + .always(function(){ + + // No longer watch for changed VM settings + $('#vboxPane').unbind('vboxEvents',machineSettingsChanged); + + }) + + // Run this when "Save" is clicked + .done(function() { + + var loader = new vboxLoader(); + var sdata = $.extend($('#vboxSettingsDialog').data('vboxMachineData'),{'clientConfig':$('#vboxPane').data('vboxConfig')}); + loader.add('machineSave',function(){return;},sdata); + loader.onLoad = function() { + // Refresh media + var mload = new vboxLoader(); + mload.add('vboxGetMedia',function(d){$('#vboxPane').data('vboxMedia',d.responseData);}); + mload.onLoad = function() { + results.resolve(); + }; + mload.run(); + }; + loader.run(); + }); + + }); + + return results.promise(); +} + + + + +/** + * Run the "First Run" wizard + * @param {Object} vm - VM details object + */ +function vboxWizardFirstRunDialog(vm) { + + // ref + var self = this; + + this.parentClass = vboxWizard; + this.parentClass(); + + this.name = 'wizardFirstRun'; + this.title = $('<div />').text(vm.name).html(); + this.bg = 'images/vbox/vmw_first_run_bg.png'; + this.icon = vboxGuestOSTypeIcon(vm.OSTypeId); + this.steps = 1; + this.finishText = trans('Start','UIWizardFirstRun'); + this.context = 'UIWizardFirstRun'; + this.noAdvanced = true; + this.args = vm; + + // This still resolves on cancel + this.onCancel = function () { + self.completed.resolve(); + }; + + this.onFinish = function() { + + var med = vboxMedia.getMediumById($('#wizardFirstRunMedia').find(":selected").attr('value')); + + $(self.dialog).empty().remove(); + + if(med) { + + var port = null; + var device = null; + var bus = null; + var controller = null; + + for(var i = 0; i < self.args.storageControllers.length; i++) { + for(var a = 0; a < self.args.storageControllers[i].mediumAttachments.length; a++) { + if(self.args.storageControllers[i].mediumAttachments[a].type == "DVD" && + self.args.storageControllers[i].mediumAttachments[a].medium == null) { + + port = self.args.storageControllers[i].mediumAttachments[a].port; + device = self.args.storageControllers[i].mediumAttachments[a].device; + bus = self.args.storageControllers[i].bus; + controller = self.args.storageControllers[i].name; + + break; + } + } + } + + + var args = {'vm':self.args.id, + 'medium':med, + 'port':port, + 'device':device, + 'bus':bus, + 'controller':controller, + 'noSave':true + }; + + // Ajax request to mount medium + var mount = new vboxLoader(); + mount.add('mediumMount',function(ret){ + var l = new vboxLoader(); + l.add('vboxGetMedia',function(d){$('#vboxPane').data('vboxMedia',d.responseData);}); + l.onLoad = function(){ + self.completed.resolve(); + }; + l.run(); + },args); + mount.run(); + + + } else { + self.completed.resolve(); + } + + }; +} + + +/** + * Display a settings dialog (generic) called by dialog initializers + * @param {String} title - title of dialog + * @param {Array} panes - list of panes {Object} to load + * @param {Object} data - list of data to load + * @param {String} pane - optionally automatically select pane when dialog is shown + * @param {String} icon - optional URL to icon for dialog + * @param {String} langContext - language context to use for translations + * @param {Function} presave - presave callback to run + * @returns {Object} deferred promise + * @see trans() + */ +function vboxSettingsDialog(title,panes,data,pane,icon,langContext,presave) { + + var results = $.Deferred(); + + var d = $('<div />').attr({'id':'vboxSettingsDialog','style':'display: none;'}); + + var f = $('<form />').attr({'name':'frmVboxSettings','style':'height: 100%'}); + + var t = $('<table />').attr({'style':'height: 100%;','class':'vboxSettingsTable'}); + + var tr = $('<tr />'); + + $($('<td />').attr({'id':'vboxSettingsMenu','style': (panes.length == 1 ? 'display:none;' : '')})).append($('<ul />').attr({'id':'vboxSettingsMenuList','class':'vboxHover'})).appendTo(tr); + + var td = $('<td />').attr({'id':'vboxSettingsPane'}).css({'height':'100%'}); + + // Settings table contains title and visible settings pane + var stbl = $('<table />').css({'height':'100%','width':'100%','padding':'0px','margin':'0px','border':'0px','border-spacing':'0px'}); + + // Title + var d1 = $('<div />').attr({'id':'vboxSettingsTitle'}).html('Padding').css({'display':(panes.length == 1 ? 'none' : '')}); + $(stbl).append($('<tr />').append($('<td />').css({'height':'1%','padding':'0px','margin':'0px','border':'0px'}).append(d1))); + + + // Settings pane + var d1 = $('<div />').attr({'id':'vboxSettingsList'}).css({'width':'100%'}); + + $(stbl).append($('<tr />').append($('<td />').css({'padding':'0px','margin':'0px','border':'0px'}).append(d1))); + + + $(td).append(stbl).appendTo(tr); + + $(d).append($(f).append($(t).append(tr))).appendTo('#vboxPane'); + + /* Load panes and data */ + var loader = new vboxLoader(); + + /* Load Data */ + for(var i = 0; i < data.length; i++) { + loader.add(data[i].fn,data[i].callback,(data[i].args ? data[i].args : undefined)); + } + + /* Load settings panes */ + for(var i = 0; i < panes.length; i++) { + + if(panes[i].disabled) continue; + + // Menu item + $('<li />').html('<div><img src="images/vbox/'+panes[i].icon+'_16px.png" /></div> <div>'+trans(panes[i].label,langContext)+'</div>').data(panes[i]).click(function(){ + + $('#vboxSettingsTitle').html(trans($(this).data('label'),langContext)); + + $(this).addClass('vboxListItemSelected').siblings().addClass('vboxListItem').removeClass('vboxListItemSelected'); + + // jquery apply this css to everything with class .settingsPa.. + $('#vboxSettingsDialog .vboxSettingsPaneSection').css({'display':'none'}); + + // Show selected pane + $('#vboxSettingsPane-' + $(this).data('name')).css({'display':''}).children().first().trigger('show'); + + }).on("mouseenter",function(){$(this).addClass('vboxHover');}).on("mouseleave",function(){$(this).removeClass('vboxHover');}).appendTo($('#vboxSettingsMenuList')); + + + // Settings pane + $('#vboxSettingsList').append($('<div />').attr({'id':'vboxSettingsPane-'+panes[i].name,'style':'display: none;','class':'vboxSettingsPaneSection ui-corner-all ' + (panes[i].tabbed ? 'vboxTabbed' : 'vboxNonTabbed')})); + + loader.addFileToDOM('panes/settings'+panes[i].name+'.html',$('#vboxSettingsPane-'+panes[i].name)); + + } + + loader.onLoad = function(){ + + + /* Init UI Items */ + for(var i = 0; i < panes.length; i++) { + vboxInitDisplay($('#vboxSettingsPane-'+panes[i].name),panes[i].context); + if(panes[i].tabbed) $('#vboxSettingsPane-'+panes[i].name).tabs(); + } + + /* Tell dialog that data is loaded */ + $('#vboxSettingsDialog').trigger('dataLoaded'); + + var buttons = { }; + buttons[trans('OK','QIMessageBox')] = function() { + + $(this).trigger('save'); + + // Does some settings pane need to do some presave + // work? (ask questions, run wizard, some other asynch task) + var promise = true; + if(presave) { + promise = presave(); + } + var dlg = this; + $.when(promise).done(function() { + results.resolve(true); + $(dlg).trigger('close').empty().remove(); + $(document).trigger('click'); + }); + }; + buttons[trans('Cancel','QIMessageBox')] = function() { + results.reject(); + $(this).trigger('close').empty().remove(); + $(document).trigger('click'); + }; + + // Init with "nothing has changed yet" + $('#vboxSettingsDialog').data('formDataChanged', false); + + // Show dialog + $('#vboxSettingsDialog').dialog({'closeOnEscape':true,'width':(panes.length > 1 ? 900 : 600),'height':(panes.length > 1 ? 500 : 450),'buttons':buttons,'modal':true,'autoOpen':true,'dialogClass':'vboxSettingsDialog vboxDialogContent','title':(icon ? '<img src="images/vbox/'+icon+'_16px.png" class="vboxDialogTitleIcon" /> ' : '') + title}).on("dialogbeforeclose",function(){ + $(this).parent().find('span:contains("'+trans('Cancel','QIMessageBox')+'")').trigger('click'); + }); + + // Resize pane + $('#vboxSettingsList').height($('#vboxSettingsList').parent().innerHeight()-8).css({'overflow':'auto','padding':'0px','margin-top':'8px','border':'0px','border-spacing':'0px'}); + + // Resizing dialog, resizes this too + $('#vboxSettingsDialog').on('dialogresizestop',function(){ + var h = $('#vboxSettingsList').css({'display':'none'}).parent().innerHeight(); + $('#vboxSettingsList').height(h-8).css({'display':''}); + }); + + /* Select first or passed menu item */ + var i = 0; + var offset = 0; + var tab = undefined; + if(typeof pane == "string") { + var section = pane.split(':'); + if(section[1]) tab = section[1]; + for(i = 0; i < panes.length; i++) { + if(panes[i].disabled) offset++; + if(panes[i].name == section[0]) break; + } + } + i-=offset; + if(i >= panes.length) i = 0; + $('#vboxSettingsMenuList').children('li:eq('+i+')').first().click().each(function(){ + if(tab !== undefined) { + // Check for out of scope tab + tab = Math.min(($('#vboxSettingsPane-'+$(this).data('name')).children('ul').first().children().length-1), parseInt(tab)); + $('#vboxSettingsPane-'+$(this).data('name')).tabs('option','active', tab); + } + + }); + + /* Only 1 pane? */ + if(panes.length == 1) { + $('#vboxSettingsDialog table.vboxSettingsTable').css('width','100%'); + $('#vboxSettingsDialog').dialog('option','title',(icon ? '<img src="images/vbox/'+icon+'_16px.png" class="vboxDialogTitleIcon" /> ' : '') + trans(panes[0].label,langContext)); + } + + + }; + + loader.run(); + + return results.promise(); + +} diff --git a/js/eventlistener.js b/js/eventlistener.js index 43de551..f98b908 100644 --- a/js/eventlistener.js +++ b/js/eventlistener.js @@ -1,250 +1,250 @@ -/**
- *
- * @fileOverview Event listener singleton. Provides vboxEventListener
- * @author Ian Moore (imoore76 at yahoo dot com)
- * @version $Id: eventlistener.js 596 2015-04-19 11:50:53Z imoore76 $
- * @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com)
- */
-
-/**
- * vboxEventListener
- *
- * Polls vboxwebsrv for pending events and triggers
- * events on $('#vboxPane')
- *
- * @namespace vboxEventListener
- */
-var vboxEventListener = {
-
- // Timeout
- timeout: 20,
-
- // Not initially running
- _running: false,
-
- // persistent request data
- _persist: {},
-
- // List of machines to subscribe to at runtime
- _subscribeList: [],
-
- // Watchdog to make sure vboxEventListener is still
- // running and attempting to get events
- _watchdog: {
- lastRun: 0,
- start: function() {
- window.setInterval(function() {
- if(vboxEventListener._running &&
- ((new Date().getTime()/1000) - vboxEventListener._watchdog.lastRun > vboxEventListener.timeout)) {
- phpVirtualBoxFailure(' (EventListener watchdog failure)');
- vboxEventListener.stop();
- window.clearInterval(vboxEventListener._watchdog.timer);
- }
- }, vboxEventListener.timeout)
- },
- stop: function() {
- window.clearInterval(vboxEventListener._watchdog.timer);
- }
- },
-
- // Since VirtualBox handles to event listener objects are persistent,
- // calls using the same handle should be synchronous
- _requestQueue: {
-
- requests: [],
- running: false,
-
- // run timer
- timer: window.setInterval(function(){
- vboxEventListener._requestQueue.run();
- }, 5000), // 5 seconds
-
- // Add a request to the queue
- addReq: function(q) {
-
- var d = $.Deferred();
-
- vboxEventListener._requestQueue.requests.push({'request':q,'deferred':d});
- vboxEventListener._requestQueue.run();
-
- return d.promise();
- },
-
- // Run through the queue
- run : function() {
-
- // Already running through queue
- if(vboxEventListener._requestQueue.running) return;
-
- vboxEventListener._requestQueue.running = true;
- vboxEventListener._requestQueue.runReq();
-
- },
-
- // Run a single request, removing it from the queue
- runReq: function() {
- var r = vboxEventListener._requestQueue.requests.shift();
- if(r) {
- $.when(r.request())
- .done(r.deferred.resolve)
- .fail(r.deferred.reject)
- .always(vboxEventListener._requestQueue.runReq);
- } else {
- vboxEventListener._requestQueue.running = false;
- }
- }
-
- },
-
- /**
- * Start event listener loop
- * @param {Array} vmlist - list of VM ids to subscribe to
- */
- start: function(vmlist) {
-
- // Already started?
- if(vboxEventListener._running) return;
-
- // Get timeout if exists
- if($('#vboxPane').data('vboxConfig').eventListenerTimeout)
- vboxEventListener.timeout = $('#vboxPane').data('vboxConfig').eventListenerTimeout;
-
- vboxEventListener._running = true;
-
- var started = $.Deferred();
-
- // Subscribe to events and start main loop
- $.when(vboxAjaxRequest('subscribeEvents',{vms:vmlist})).done(function(d) {
- vboxEventListener._persist = d.persist;
- $.when(vboxEventListener._getEvents()).done(function(){
- vboxEventListener._watchdog.start();
- started.resolve();
- });
- });
-
- return started.promise();
-
- },
-
- /**
- * Subscribe to a single machine's events. This should happen
- *
- * @param {String} vmid - ID of VM to subscribe to
- */
- subscribeVMEvents: function(vmid) {
-
- // Push into list
- vboxEventListener._subscribeList.push(vmid);
-
- // Add subscription request to queue
- return vboxEventListener._requestQueue.addReq(function(){
-
- if(!vboxEventListener._subscribeList.length) return;
-
- var vms = vboxEventListener._subscribeList.concat();
- vboxEventListener._subscribeList = [];
-
- var vmEvents = $.Deferred();
- $.when(vboxAjaxRequest('machineSubscribeEvents', {'vms':vms},{'persist':vboxEventListener._persist})).done(function(d){
- // Always set persistent request data
- vboxEventListener._persist = d.persist;
- }).always(function(){
- vmEvents.resolve();
- });
- return vmEvents.promise();
- });
-
- },
-
- /**
- * Stop event listener loop and unsubscribe from events
- */
- stop: function() {
-
- if(!vboxEventListener._running)
- return;
-
- window.clearTimeout(vboxEventListener._running);
- vboxEventListener._running = false;
-
- vboxEventListener._watchdog.stop();
-
- // Unsubscribe from events. Returns a deferred object
- return vboxEventListener._requestQueue.addReq(function(){
- return vboxAjaxRequest('unsubscribeEvents', {}, {'persist':vboxEventListener._persist});
- });
-
- },
-
- /**
- * Main loop - get pending events
- */
- _getEvents: function(){
-
- // Don't do anything if we aren't running anymore
- if(!vboxEventListener._running) return;
-
- // Add to queue
- return vboxEventListener._requestQueue.addReq(function(){
-
- return $.when(new Date().getTime(), vboxAjaxRequest('getEvents',{}, {'persist':vboxEventListener._persist})).done(function(lastTime,d) {
-
- // Don't do anything if this is not running
- if(!vboxEventListener._running) return;
-
- // Check for valid result
- if(!d || !d.success) {
- if(vboxEventListener._running)
- phpVirtualBoxFailure();
- return;
- }
-
-
- // Check key to make sure this isn't a stale
- // response from a previously selected server
- if(!d.key || (d.key != $('#vboxPane').data('vboxConfig').key)) return;
-
- // Tell the watch dog that we were run
- vboxEventListener._watchdog.lastRun = (new Date().getTime() / 1000);
-
- // Always set persistent request data
- vboxEventListener._persist = d.persist;
-
- // Loop through each event triggering changes
- if(d.responseData && d.responseData.length) {
-
- // Trigger each event individually
- for(var i = 0; i < d.responseData.length; i++) {
-
- // Trigger raw vbox events
- $('#vboxPane').trigger('vbox' + d.responseData[i].eventType, [d.responseData[i]]);
-
- }
-
- // Trigger event list queue
- $('#vboxPane').trigger('vboxEvents', [d.responseData]);
-
- }
-
- // Wait at most 3 seconds
- var wait = Math.min(3000,3000 - ((new Date().getTime()) - lastTime));
- if(wait <= 0) {
- vboxEventListener._running = true;
- vboxEventListener._getEvents();
- }
- else {
- vboxEventListener._running = window.setTimeout(vboxEventListener._getEvents, wait);
- }
-
- });
-
- });
- }
-};
-
-// Stop event listener on window unload
-$(document).ready(function() {
- $(window).on('unload',function() {
- vboxEventListener.stop();
- });
-});
+/** + * + * @fileOverview Event listener singleton. Provides vboxEventListener + * @author Ian Moore (imoore76 at yahoo dot com) + * @version $Id: eventlistener.js 596 2015-04-19 11:50:53Z imoore76 $ + * @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com) + */ + +/** + * vboxEventListener + * + * Polls vboxwebsrv for pending events and triggers + * events on $('#vboxPane') + * + * @namespace vboxEventListener + */ +var vboxEventListener = { + + // Timeout + timeout: 20, + + // Not initially running + _running: false, + + // persistent request data + _persist: {}, + + // List of machines to subscribe to at runtime + _subscribeList: [], + + // Watchdog to make sure vboxEventListener is still + // running and attempting to get events + _watchdog: { + lastRun: 0, + start: function() { + window.setInterval(function() { + if(vboxEventListener._running && + ((new Date().getTime()/1000) - vboxEventListener._watchdog.lastRun > vboxEventListener.timeout)) { + phpVirtualBoxFailure(' (EventListener watchdog failure)'); + vboxEventListener.stop(); + window.clearInterval(vboxEventListener._watchdog.timer); + } + }, vboxEventListener.timeout) + }, + stop: function() { + window.clearInterval(vboxEventListener._watchdog.timer); + } + }, + + // Since VirtualBox handles to event listener objects are persistent, + // calls using the same handle should be synchronous + _requestQueue: { + + requests: [], + running: false, + + // run timer + timer: window.setInterval(function(){ + vboxEventListener._requestQueue.run(); + }, 5000), // 5 seconds + + // Add a request to the queue + addReq: function(q) { + + var d = $.Deferred(); + + vboxEventListener._requestQueue.requests.push({'request':q,'deferred':d}); + vboxEventListener._requestQueue.run(); + + return d.promise(); + }, + + // Run through the queue + run : function() { + + // Already running through queue + if(vboxEventListener._requestQueue.running) return; + + vboxEventListener._requestQueue.running = true; + vboxEventListener._requestQueue.runReq(); + + }, + + // Run a single request, removing it from the queue + runReq: function() { + var r = vboxEventListener._requestQueue.requests.shift(); + if(r) { + $.when(r.request()) + .done(r.deferred.resolve) + .fail(r.deferred.reject) + .always(vboxEventListener._requestQueue.runReq); + } else { + vboxEventListener._requestQueue.running = false; + } + } + + }, + + /** + * Start event listener loop + * @param {Array} vmlist - list of VM ids to subscribe to + */ + start: function(vmlist) { + + // Already started? + if(vboxEventListener._running) return; + + // Get timeout if exists + if($('#vboxPane').data('vboxConfig').eventListenerTimeout) + vboxEventListener.timeout = $('#vboxPane').data('vboxConfig').eventListenerTimeout; + + vboxEventListener._running = true; + + var started = $.Deferred(); + + // Subscribe to events and start main loop + $.when(vboxAjaxRequest('subscribeEvents',{vms:vmlist})).done(function(d) { + vboxEventListener._persist = d.persist; + $.when(vboxEventListener._getEvents()).done(function(){ + vboxEventListener._watchdog.start(); + started.resolve(); + }); + }); + + return started.promise(); + + }, + + /** + * Subscribe to a single machine's events. This should happen + * + * @param {String} vmid - ID of VM to subscribe to + */ + subscribeVMEvents: function(vmid) { + + // Push into list + vboxEventListener._subscribeList.push(vmid); + + // Add subscription request to queue + return vboxEventListener._requestQueue.addReq(function(){ + + if(!vboxEventListener._subscribeList.length) return; + + var vms = vboxEventListener._subscribeList.concat(); + vboxEventListener._subscribeList = []; + + var vmEvents = $.Deferred(); + $.when(vboxAjaxRequest('machineSubscribeEvents', {'vms':vms},{'persist':vboxEventListener._persist})).done(function(d){ + // Always set persistent request data + vboxEventListener._persist = d.persist; + }).always(function(){ + vmEvents.resolve(); + }); + return vmEvents.promise(); + }); + + }, + + /** + * Stop event listener loop and unsubscribe from events + */ + stop: function() { + + if(!vboxEventListener._running) + return; + + window.clearTimeout(vboxEventListener._running); + vboxEventListener._running = false; + + vboxEventListener._watchdog.stop(); + + // Unsubscribe from events. Returns a deferred object + return vboxEventListener._requestQueue.addReq(function(){ + return vboxAjaxRequest('unsubscribeEvents', {}, {'persist':vboxEventListener._persist}); + }); + + }, + + /** + * Main loop - get pending events + */ + _getEvents: function(){ + + // Don't do anything if we aren't running anymore + if(!vboxEventListener._running) return; + + // Add to queue + return vboxEventListener._requestQueue.addReq(function(){ + + return $.when(new Date().getTime(), vboxAjaxRequest('getEvents',{}, {'persist':vboxEventListener._persist})).done(function(lastTime,d) { + + // Don't do anything if this is not running + if(!vboxEventListener._running) return; + + // Check for valid result + if(!d || !d.success) { + if(vboxEventListener._running) + phpVirtualBoxFailure(); + return; + } + + + // Check key to make sure this isn't a stale + // response from a previously selected server + if(!d.key || (d.key != $('#vboxPane').data('vboxConfig').key)) return; + + // Tell the watch dog that we were run + vboxEventListener._watchdog.lastRun = (new Date().getTime() / 1000); + + // Always set persistent request data + vboxEventListener._persist = d.persist; + + // Loop through each event triggering changes + if(d.responseData && d.responseData.length) { + + // Trigger each event individually + for(var i = 0; i < d.responseData.length; i++) { + + // Trigger raw vbox events + $('#vboxPane').trigger('vbox' + d.responseData[i].eventType, [d.responseData[i]]); + + } + + // Trigger event list queue + $('#vboxPane').trigger('vboxEvents', [d.responseData]); + + } + + // Wait at most 3 seconds + var wait = Math.min(3000,3000 - ((new Date().getTime()) - lastTime)); + if(wait <= 0) { + vboxEventListener._running = true; + vboxEventListener._getEvents(); + } + else { + vboxEventListener._running = window.setTimeout(vboxEventListener._getEvents, wait); + } + + }); + + }); + } +}; + +// Stop event listener on window unload +$(document).ready(function() { + $(window).on('unload',function() { + vboxEventListener.stop(); + }); +}); diff --git a/js/jquery.jec-1.3.1.js b/js/jquery.jec-1.3.1.js index 33acd4b..3065fca 100644 --- a/js/jquery.jec-1.3.1.js +++ b/js/jquery.jec-1.3.1.js @@ -1,861 +1,861 @@ -/**
- * jQuery jEC (jQuery Editable Combobox) 1.3.1
- * http://code.google.com/p/jquery-jec
- *
- * Copyright (c) 2008-2009 Lukasz Rajchel (lukasz@rajchel.pl | http://rajchel.pl)
- * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
- * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
- *
- * Documentation : http://code.google.com/p/jquery-jec/wiki/Documentation
- * Changelog : http://code.google.com/p/jquery-jec/wiki/Changelog
- *
- * Contributors : Lukasz Rajchel, Artem Orlov
- */
-
-/*jslint white: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true,
-bitwise: true, regexp: true, strict: true, newcap: true, immed: true, maxerr: 50, indent: 4,
-maxlen: 100*/
-/*global Array, Math, String, clearInterval, document, jQuery, setInterval*/
-/*members ':', Handle, Remove, Set, acceptedKeys, addClass, all, append, appendTo, array, attr,
-before, bind, blinkingCursor, blinkingCursorInterval, blur, bool, browser, ceil, change, charCode,
-classes, clearCursor, click, css, cursorState, data, destroy, disable, each, editable, enable, eq,
-expr, extend, filter, find, floor, fn, focus, focusOnNewOption, fromCharCode, get, getId,
-handleCursor, ignoredKeys, ignoreOptGroups, inArray, init, initJS, integer, isArray, isPlainObject,
-jEC, jECTimer, jec, jecKill, jecOff, jecOn, jecPref, jecValue, keyCode, keyDown, keyPress,
-keyRange, keyUp, keys, length, max, maxLength, min, msie, object, openedState, optionClasses,
-optionStyles, parent, position, pref, push, random, remove, removeAttr, removeClass, removeData,
-safari, setEditableOption, styles, substring, text, trigger triggerChangeEvent, unbind, uneditable,
-useExistingOptions, val, value, valueIsEditable*/
-'use strict';
-(function ($) {
-
- $.jEC = (function () {
- var pluginClass = 'jecEditableOption', cursorClass = 'hasCursor', options = {},
- values = {}, lastKeyCode, defaults, Validators, EventHandlers, Combobox,
- activeCombobox;
-
- defaults = {
- position: 0,
- ignoreOptGroups: false,
- maxLength: 255,
- classes: [],
- styles: {},
- optionClasses: [],
- optionStyles: {},
- triggerChangeEvent: false,
- focusOnNewOption: false,
- useExistingOptions: false,
- blinkingCursor: false,
- blinkingCursorInterval: 1000,
- ignoredKeys: [],
- acceptedKeys: [[32, 126], [191, 382]]
- };
-
- Validators = (function () {
- return {
- integer: function (value) {
- return typeof value === 'number' && Math.ceil(value) === Math.floor(value);
- },
-
- keyRange: function (value) {
- var min, max;
- if (typeof value === 'object' && !$.isArray(value)) {
- min = value.min;
- max = value.max;
- } else if ($.isArray(value) && value.length === 2) {
- min = value[0];
- max = value[1];
- }
- return Validators.integer(min) && Validators.integer(max) && min <= max;
- }
- };
- }());
-
- EventHandlers = (function () {
- var getKeyCode;
-
- getKeyCode = function (event) {
- var charCode = event.charCode;
- if (charCode !== undefined && charCode !== 0) {
- return charCode;
- } else {
- return event.keyCode;
- }
- };
-
- return {
- // focus event handler
- // enables blinking cursor
- focus: function (event) {
- var opt = options[Combobox.getId($(this))];
- if (opt.blinkingCursor && $.jECTimer === undefined) {
- activeCombobox = $(this);
- $.jECTimer = setInterval($.jEC.handleCursor, opt.blinkingCursorInterval);
- }
- },
-
- // blur event handler
- // disables blinking cursor
- blur: function (event) {
- if ($.jECTimer !== undefined) {
- clearInterval($.jECTimer);
- $.jECTimer = undefined;
- activeCombobox = undefined;
- Combobox.clearCursor($(this));
- }
- Combobox.openedState($(this), false);
- },
-
- // keydown event handler
- // handles keys pressed on select (backspace and delete must be handled
- // in keydown event in order to work in IE)
- keyDown: function (event) {
- var keyCode = getKeyCode(event), option, value;
-
- lastKeyCode = keyCode;
-
- switch (keyCode) {
- case 8: // backspace
- case 46: // delete
- option = $(this).find('option.' + pluginClass);
- if (option.val().length >= 1) {
- value = option.text().substring(0, option.text().length - 1);
- option.val(value).text(value).prop('selected', true);
- }
- return (keyCode !== 8);
- default:
- break;
- }
- },
-
- // keypress event handler
- // handles the rest of the keys (keypress event gives more informations
- // about pressed keys)
- keyPress: function (event) {
- var keyCode = getKeyCode(event), opt = options[Combobox.getId($(this))],
- option, value, specialKeys, exit = false, text;
-
- Combobox.clearCursor($(this));
- if (keyCode !== 9 && keyCode !== 13 && keyCode !== 27) {
- // special keys codes
- specialKeys = [37, 38, 39, 40, 46];
- // handle special keys
- $.each(specialKeys, function (i, val) {
- if (keyCode === val && keyCode === lastKeyCode) {
- exit = true;
- }
- });
-
- // don't handle ignored keys
- if (!exit && $.inArray(keyCode, opt.ignoredKeys) === -1) {
-
- // remove selection from all options
- $(this).find('option:selected').prop('selected',false);
-
- if ($.inArray(keyCode, opt.acceptedKeys) !== -1) {
- option = $(this).find('option.' + pluginClass).first();
- text = option.text();
-
- if (text.length < opt.maxLength) {
- value = text + String.fromCharCode(keyCode);
- option.val(value).text(value).attr('label',value);
- }
-
- option.prop('selected', true);
- }
- }
-
- return false;
- }
- },
-
- keyUp: function (event) {
- var opt = options[Combobox.getId($(this))];
- if (opt.triggerChangeEvent) {
- $(this).trigger('change');
- }
- },
-
- // change event handler
- // handles editable option changing based on a pre-existing values
- change: function () {
- var opt = options[Combobox.getId($(this))];
- if (opt.useExistingOptions) {
- Combobox.setEditableOption($(this));
- }
- },
-
- click: function () {
- if (!$.browser.safari) {
- Combobox.openedState($(this), !Combobox.openedState($(this)));
- }
- }
- };
- }());
-
- // Combobox
- Combobox = (function () {
- var Parameters, EditableOption, generateId, setup;
-
- // validates and set combobox parameters
- Parameters = (function () {
- var Set, Remove, Handle;
-
- Set = (function () {
- var parseKeys, Handles;
-
- parseKeys = function (value) {
- var keys = [];
- if ($.isArray(value)) {
- $.each(value, function (i, val) {
- var j, min, max;
- if (Validators.keyRange(val)) {
- if ($.isArray(val)) {
- min = val[0];
- max = val[1];
- } else {
- min = val.min;
- max = val.max;
- }
- for (j = min; j <= max; j += 1) {
- keys.push(j);
- }
- } else if (typeof val === 'number' && Validators.integer(val)) {
- keys.push(val);
- }
- });
- }
- return keys;
- };
-
- Handles = (function () {
- return {
- integer: function (elem, name, value) {
- var id = Combobox.getId(elem), opt = options[id];
- if (opt !== undefined && Validators.integer(value)) {
- opt[name] = value;
- return true;
- }
- return false;
- },
- bool: function (elem, name, value) {
- var id = Combobox.getId(elem), opt = options[id];
- if (opt !== undefined && typeof value === 'boolean') {
- opt[name] = value;
- return true;
- }
- return false;
- },
- array: function (elem, name, value) {
- if (typeof value === 'string') {
- value = [value];
- }
- var id = Combobox.getId(elem), opt = options[id];
- if (opt !== undefined && $.isArray(value)) {
- opt[name] = value;
- return true;
- }
- return false;
- },
- object: function (elem, name, value) {
- var id = Combobox.getId(elem), opt = options[id];
- if (opt !== undefined && value !== null &&
- typeof value === 'object' && !$.isArray(value)) {
- opt[name] = value;
- }
- },
- keys: function (elem, name, value) {
- var id = Combobox.getId(elem), opt = options[id];
- if (opt !== undefined && $.isArray(value)) {
- opt[name] = parseKeys(value);
- }
- }
- };
- }());
-
- return {
- position: function (elem, value) {
- if (Handles.integer(elem, 'position', value)) {
- var id = Combobox.getId(elem), opt = options[id], optionsCount;
- optionsCount =
- elem.find('option:not(.' + pluginClass + ')').length;
- if (value > optionsCount) {
- opt.position = optionsCount;
- }
- }
- },
-
- ignoreOptGroups: function (elem, value) {
- Handles.bool(elem, 'ignoreOptGroups', value);
- },
-
- maxLength: function (elem, value) {
- if (Handles.integer(elem, 'maxLength', value)) {
- var id = Combobox.getId(elem), opt = options[id];
- if (value < 0 || value > 255) {
- opt.maxLength = 255;
- }
- }
- },
-
- classes: function (elem, value) {
- Handles.array(elem, 'classes', value);
- },
-
- optionClasses: function (elem, value) {
- Handles.array(elem, 'optionClasses', value);
- },
-
- styles: function (elem, value) {
- Handles.object(elem, 'styles', value);
- },
-
- optionStyles: function (elem, value) {
- Handles.object(elem, 'optionStyles', value);
- },
-
- triggerChangeEvent: function (elem, value) {
- Handles.bool(elem, 'triggerChangeEvent', value);
- },
-
- focusOnNewOption: function (elem, value) {
- Handles.bool(elem, 'focusOnNewOption', value);
- },
-
- useExistingOptions: function (elem, value) {
- Handles.bool(elem, 'useExistingOptions', value);
- },
-
- blinkingCursor: function (elem, value) {
- Handles.bool(elem, 'blinkingCursor', value);
- },
-
- blinkingCursorInterval: function (elem, value) {
- Handles.integer(elem, 'blinkingCursorInterval', value);
- },
-
- ignoredKeys: function (elem, value) {
- Handles.keys(elem, 'ignoredKeys', value);
- },
-
- acceptedKeys: function (elem, value) {
- Handles.keys(elem, 'acceptedKeys', value);
- }
- };
- }());
-
- Remove = (function () {
- var removeClasses, removeStyles;
-
- removeClasses = function (elem, classes) {
- $.each(classes, function (i, val) {
- elem.removeClass(val);
- });
- };
-
- removeStyles = function (elem, styles) {
- $.each(styles, function (key, val) {
- elem.css(key, '');
- });
- };
-
- return {
- classes: function (elem) {
- var id = Combobox.getId(elem), opt = options[id];
- if (opt !== undefined) {
- removeClasses(elem, opt.classes);
- }
- },
-
- optionClasses: function (elem) {
- var id = Combobox.getId(elem), opt = options[id];
- if (opt !== undefined) {
- removeClasses(elem.find('option.' + pluginClass),
- opt.optionClasses);
- }
- },
-
- styles: function (elem) {
- var id = Combobox.getId(elem), opt = options[id];
- if (opt !== undefined) {
- removeStyles(elem, opt.styles);
- }
- },
-
- optionStyles: function (elem) {
- var id = Combobox.getId(elem), opt = options[id];
- if (opt !== undefined) {
- removeStyles(elem.find('option.' + pluginClass),
- opt.optionStyles);
- }
- },
-
- all: function (elem) {
- Remove.classes(elem);
- Remove.optionClasses(elem);
- Remove.styles(elem);
- Remove.optionStyles(elem);
- }
- };
- }());
-
- Handle = (function () {
- var setClasses, setStyles;
-
- setClasses = function (elem, classes) {
- $.each(classes, function (i, val) {
- elem.addClass(val);
- });
- };
-
- setStyles = function (elem, styles) {
- $.each(styles, function (key, val) {
- elem.css(key, val);
- });
- };
-
- return {
- position: function (elem) {
- var opt = options[Combobox.getId(elem)], option, uneditableOptions,
- container;
- option = elem.find('option.' + pluginClass);
-
- uneditableOptions = elem.find('option:not(.' + pluginClass + ')');
- if (opt.position < uneditableOptions.length) {
- container = uneditableOptions.eq(opt.position);
-
- if (!opt.ignoreOptGroups &&
- container.parent('optgroup').length > 0) {
- uneditableOptions.eq(opt.position).parent().before(option);
- } else {
- uneditableOptions.eq(opt.position).before(option);
- }
- } else {
- elem.append(option);
- }
- },
-
- classes: function (elem) {
- var id = Combobox.getId(elem), opt = options[id];
- if (opt !== undefined) {
- setClasses(elem, opt.classes);
- }
- },
-
- optionClasses: function (elem) {
- var id = Combobox.getId(elem), opt = options[id];
- if (opt !== undefined) {
- setClasses(elem.find('option.' + pluginClass),
- opt.optionClasses);
- }
- },
-
- styles: function (elem) {
- var id = Combobox.getId(elem), opt = options[id];
- if (opt !== undefined) {
- setStyles(elem, opt.styles);
- }
- },
-
- optionStyles: function (elem) {
- var id = Combobox.getId(elem), opt = options[id];
- if (opt !== undefined) {
- setStyles(elem.find('option.' + pluginClass),
- opt.optionStyles);
- }
- },
-
- focusOnNewOption: function (elem) {
- var id = Combobox.getId(elem), opt = options[id];
- if (opt !== undefined && opt.focusOnNewOption) {
- elem.find('option.' + pluginClass)
- .prop('selected', true);
- }
- },
-
- useExistingOptions: function (elem) {
- var id = Combobox.getId(elem), opt = options[id];
- if (opt !== undefined && opt.useExistingOptions) {
- Combobox.setEditableOption(elem);
- }
- },
-
- all: function (elem) {
- Handle.position(elem);
- Handle.classes(elem);
- Handle.optionClasses(elem);
- Handle.styles(elem);
- Handle.optionStyles(elem);
- Handle.focusOnNewOption(elem);
- Handle.useExistingOptions(elem);
- }
- };
- }());
-
- return {
- Set: Set,
- Remove: Remove,
- Handle: Handle
- };
- }());
-
- EditableOption = (function () {
- return {
- init: function (elem) {
- if (!elem.find('option.' + pluginClass).length) {
- var editableOption = $('<option>');
- editableOption.addClass(pluginClass);
- elem.append(editableOption);
- }
-
- elem.on('keydown', EventHandlers.keyDown);
- elem.on('keypress', EventHandlers.keyPress);
- elem.on('keyup', EventHandlers.keyUp);
- elem.on('change', EventHandlers.change);
- elem.on('focus', EventHandlers.focus);
- elem.on('blur', EventHandlers.blur);
- elem.on('click', EventHandlers.click);
- },
-
- destroy: function (elem) {
- elem.find('option.' + pluginClass).remove();
- elem.off('keydown', EventHandlers.keyDown);
- elem.off('keypress', EventHandlers.keyPress);
- elem.off('keyup', EventHandlers.keyUp);
- elem.off('change', EventHandlers.change);
- elem.off('focus', EventHandlers.focus);
- elem.off('blur', EventHandlers.blur);
- elem.off('click', EventHandlers.click);
- }
- };
- }());
-
- // generates unique identifier
- generateId = function () {
- while (true) {
- var random = Math.floor(Math.random() * 100000);
-
- if (options[random] === undefined) {
- return random;
- }
- }
- };
-
- // sets combobox
- setup = function (elem) {
- EditableOption.init(elem);
- Parameters.Handle.all(elem);
- };
-
- // Combobox public members
- return {
- // create editable combobox
- init: function (settings) {
- return $(this).filter(':uneditable').each(function () {
-
- var id = generateId(), elem = $(this);
-
- elem.data('jecId', id);
-
- // override passed default options
- options[id] = $.extend(true, {}, defaults);
-
- // parse keys
- Parameters.Set.ignoredKeys(elem, options[id].ignoredKeys);
- Parameters.Set.acceptedKeys(elem, options[id].acceptedKeys);
-
- if (typeof settings === 'object' && !$.isArray(settings)) {
- $.each(settings, function (key, val) {
- if (val !== undefined) {
- switch (key) {
- case 'position':
- Parameters.Set.position(elem, val);
- break;
- case 'ignoreOptGroups':
- Parameters.Set.ignoreOptGroups(elem, val);
- break;
- case 'maxLength':
- Parameters.Set.maxLength(elem, val);
- break;
- case 'classes':
- Parameters.Set.classes(elem, val);
- break;
- case 'optionClasses':
- Parameters.Set.optionClasses(elem, val);
- break;
- case 'styles':
- Parameters.Set.styles(elem, val);
- break;
- case 'optionStyles':
- Parameters.Set.optionStyles(elem, val);
- break;
- case 'triggerChangeEvent':
- Parameters.Set.triggerChangeEvent(elem, val);
- break;
- case 'focusOnNewOption':
- Parameters.Set.focusOnNewOption(elem, val);
- break;
- case 'useExistingOptions':
- Parameters.Set.useExistingOptions(elem, val);
- break;
- case 'blinkingCursor':
- Parameters.Set.blinkingCursor(elem, val);
- break;
- case 'blinkingCursorInterval':
- Parameters.Set.blinkingCursorInterval(elem, val);
- break;
- case 'ignoredKeys':
- Parameters.Set.ignoredKeys(elem, val);
- break;
- case 'acceptedKeys':
- Parameters.Set.acceptedKeys(elem, val);
- break;
- }
- }
- });
- }
-
- setup($(this));
- });
- },
-
- // creates editable combobox without using existing select elements
- initJS: function (options, settings) {
- var select, addOptions;
-
- select = $('<select>');
-
- addOptions = function (elem, options) {
- if ($.isArray(options)) {
- $.each(options, function (i, val) {
- if ($.isPlainObject(val)) {
- $.each(val, function (key, value) {
- if ($.isArray(value)) {
- var og = $('<optgroup>').attr('label', key);
- addOptions(og, value);
- og.appendTo(select);
- } else if (typeof value === 'number' ||
- typeof value === 'string') {
- $('<option>').text(value).prop('value', key)
- .appendTo(elem);
- }
- });
- } else if (typeof val === 'string' || typeof val === 'number') {
- $('<option>').text(val).prop('value', val).appendTo(elem);
- }
- });
- }
- };
-
- addOptions(select, options);
-
- return select.jec(settings);
- },
-
- // destroys editable combobox
- destroy: function () {
- return $(this).filter(':editable').each(function () {
- $(this).jecOff();
- $.removeData($(this).get(0), 'jecId');
- $.removeData($(this).get(0), 'jecCursorState');
- $.removeData($(this).get(0), 'jecOpenedState');
- });
- },
-
- // enable editablecombobox
- enable: function () {
- return $(this).filter(':editable').each(function () {
- var id = Combobox.getId($(this)), value = values[id];
-
- setup($(this));
-
- if (value !== undefined) {
- $(this).jecValue(value);
- }
- });
- },
-
- // disable editable combobox
- disable: function () {
- return $(this).filter(':editable').each(function () {
- var val = $(this).find('option.' + pluginClass).val();
- values[Combobox.getId($(this))] = val;
- Parameters.Remove.all($(this));
- EditableOption.destroy($(this));
- });
- },
-
- // gets or sets editable option's value
- value: function (value, setFocus) {
- if ($(this).filter(':editable').length > 0) {
- if (value === null || value === undefined) {
- // get value
- return $(this).find('option.' + pluginClass).val();
- } else if (typeof value === 'string' || typeof value === 'number') {
- // set value
- return $(this).filter(':editable').each(function () {
- var option = $(this).find('option.' + pluginClass);
- option.val(value).text(value);
- if (typeof setFocus !== 'boolean' || setFocus) {
- option.prop('selected', true);
- }
- });
- }
- }
- },
-
- // gets or sets editable option's preference
- pref: function (name, value) {
- if ($(this).filter(':editable').length > 0) {
- if (typeof name === 'string') {
- if (value === null || value === undefined) {
- // get preference
- return options[Combobox.getId($(this))][name];
- } else {
- // set preference
- return $(this).filter(':editable').each(function () {
- switch (name) {
- case 'position':
- Parameters.Set.position($(this), value);
- Parameters.Handle.position($(this));
- break;
- case 'classes':
- Parameters.Remove.classes($(this));
- Parameters.Set.classes($(this), value);
- Parameters.Handle.position($(this));
- break;
- case 'optionClasses':
- Parameters.Remove.optionClasses($(this));
- Parameters.Set.optionClasses($(this), value);
- Parameters.Set.optionClasses($(this));
- break;
- case 'styles':
- Parameters.Remove.styles($(this));
- Parameters.Set.styles($(this), value);
- Parameters.Set.styles($(this));
- break;
- case 'optionStyles':
- Parameters.Remove.optionStyles($(this));
- Parameters.Set.optionStyles($(this), value);
- Parameters.Handle.optionStyles($(this));
- break;
- case 'focusOnNewOption':
- Parameters.Set.focusOnNewOption($(this), value);
- Parameters.Handle.focusOnNewOption($(this));
- break;
- case 'useExistingOptions':
- Parameters.Set.useExistingOptions($(this), value);
- Parameters.Handle.useExistingOptions($(this));
- break;
- case 'blinkingCursor':
- Parameters.Set.blinkingCursor($(this), value);
- break;
- case 'blinkingCursorInterval':
- Parameters.Set.blinkingCursorInterval($(this), value);
- break;
- case 'ignoredKeys':
- Parameters.Set.ignoredKeys($(this), value);
- break;
- case 'acceptedKeys':
- Parameters.Set.acceptedKeys($(this), value);
- break;
- }
- });
- }
- }
- }
- },
-
- // sets editable option to the value of currently selected option
- setEditableOption: function (elem) {
- var value = elem.find('option:selected').text();
- elem.find('option.' + pluginClass).prop('value', elem.val())
- .text(value).prop('selected', true);
- },
-
- // get combobox id
- getId: function (elem) {
- return elem.data('jecId');
- },
-
- valueIsEditable: function (elem) {
- return elem.find('option.' + pluginClass).get(0) ===
- elem.find('option:selected').get(0);
- },
-
- clearCursor: function (elem) {
- $(elem).find('option.' + cursorClass).each(function () {
- var text = $(this).text();
- $(this).removeClass(cursorClass).text(text.substring(0, text.length - 1));
- });
- },
-
- cursorState: function (elem, state) {
- return elem.data('jecCursorState', state);
- },
-
- openedState: function (elem, state) {
- return elem.data('jecOpenedState', state);
- },
-
- //handles editable cursor
- handleCursor: function () {
- if (activeCombobox !== undefined && activeCombobox !== null) {
- if ($.browser.msie && Combobox.openedState(activeCombobox)) {
- return;
- }
-
- var state = Combobox.cursorState(activeCombobox), elem;
- if (state) {
- Combobox.clearCursor(activeCombobox);
- } else if (Combobox.valueIsEditable(activeCombobox)) {
- elem = activeCombobox.find('option:selected');
- elem.addClass(cursorClass).text(elem.text() + '|');
- }
- Combobox.cursorState(activeCombobox, !state);
- }
- }
- };
- }());
-
- // jEC public members
- return {
- init: Combobox.init,
- enable: Combobox.enable,
- disable: Combobox.disable,
- destroy: Combobox.destroy,
- value: Combobox.value,
- pref: Combobox.pref,
- initJS: Combobox.initJS,
- handleCursor: Combobox.handleCursor
- };
- }());
-
- // register functions
- $.fn.extend({
- jec: $.jEC.init,
- jecOn: $.jEC.enable,
- jecOff: $.jEC.disable,
- jecKill: $.jEC.destroy,
- jecValue: $.jEC.value,
- jecPref: $.jEC.pref
- });
-
- $.extend({
- jec: $.jEC.initJS
- });
-
- // register selectors
- $.extend($.expr[':'], {
- editable: function (a) {
- var data = $(a).data('jecId');
- return data !== null && data !== undefined;
- },
-
- uneditable: function (a) {
- var data = $(a).data('jecId');
- return data === null || data === undefined;
- }
- });
-
+/** + * jQuery jEC (jQuery Editable Combobox) 1.3.1 + * http://code.google.com/p/jquery-jec + * + * Copyright (c) 2008-2009 Lukasz Rajchel (lukasz@rajchel.pl | http://rajchel.pl) + * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) + * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. + * + * Documentation : http://code.google.com/p/jquery-jec/wiki/Documentation + * Changelog : http://code.google.com/p/jquery-jec/wiki/Changelog + * + * Contributors : Lukasz Rajchel, Artem Orlov + */ + +/*jslint white: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, +bitwise: true, regexp: true, strict: true, newcap: true, immed: true, maxerr: 50, indent: 4, +maxlen: 100*/ +/*global Array, Math, String, clearInterval, document, jQuery, setInterval*/ +/*members ':', Handle, Remove, Set, acceptedKeys, addClass, all, append, appendTo, array, attr, +before, bind, blinkingCursor, blinkingCursorInterval, blur, bool, browser, ceil, change, charCode, +classes, clearCursor, click, css, cursorState, data, destroy, disable, each, editable, enable, eq, +expr, extend, filter, find, floor, fn, focus, focusOnNewOption, fromCharCode, get, getId, +handleCursor, ignoredKeys, ignoreOptGroups, inArray, init, initJS, integer, isArray, isPlainObject, +jEC, jECTimer, jec, jecKill, jecOff, jecOn, jecPref, jecValue, keyCode, keyDown, keyPress, +keyRange, keyUp, keys, length, max, maxLength, min, msie, object, openedState, optionClasses, +optionStyles, parent, position, pref, push, random, remove, removeAttr, removeClass, removeData, +safari, setEditableOption, styles, substring, text, trigger triggerChangeEvent, unbind, uneditable, +useExistingOptions, val, value, valueIsEditable*/ +'use strict'; +(function ($) { + + $.jEC = (function () { + var pluginClass = 'jecEditableOption', cursorClass = 'hasCursor', options = {}, + values = {}, lastKeyCode, defaults, Validators, EventHandlers, Combobox, + activeCombobox; + + defaults = { + position: 0, + ignoreOptGroups: false, + maxLength: 255, + classes: [], + styles: {}, + optionClasses: [], + optionStyles: {}, + triggerChangeEvent: false, + focusOnNewOption: false, + useExistingOptions: false, + blinkingCursor: false, + blinkingCursorInterval: 1000, + ignoredKeys: [], + acceptedKeys: [[32, 126], [191, 382]] + }; + + Validators = (function () { + return { + integer: function (value) { + return typeof value === 'number' && Math.ceil(value) === Math.floor(value); + }, + + keyRange: function (value) { + var min, max; + if (typeof value === 'object' && !$.isArray(value)) { + min = value.min; + max = value.max; + } else if ($.isArray(value) && value.length === 2) { + min = value[0]; + max = value[1]; + } + return Validators.integer(min) && Validators.integer(max) && min <= max; + } + }; + }()); + + EventHandlers = (function () { + var getKeyCode; + + getKeyCode = function (event) { + var charCode = event.charCode; + if (charCode !== undefined && charCode !== 0) { + return charCode; + } else { + return event.keyCode; + } + }; + + return { + // focus event handler + // enables blinking cursor + focus: function (event) { + var opt = options[Combobox.getId($(this))]; + if (opt.blinkingCursor && $.jECTimer === undefined) { + activeCombobox = $(this); + $.jECTimer = setInterval($.jEC.handleCursor, opt.blinkingCursorInterval); + } + }, + + // blur event handler + // disables blinking cursor + blur: function (event) { + if ($.jECTimer !== undefined) { + clearInterval($.jECTimer); + $.jECTimer = undefined; + activeCombobox = undefined; + Combobox.clearCursor($(this)); + } + Combobox.openedState($(this), false); + }, + + // keydown event handler + // handles keys pressed on select (backspace and delete must be handled + // in keydown event in order to work in IE) + keyDown: function (event) { + var keyCode = getKeyCode(event), option, value; + + lastKeyCode = keyCode; + + switch (keyCode) { + case 8: // backspace + case 46: // delete + option = $(this).find('option.' + pluginClass); + if (option.val().length >= 1) { + value = option.text().substring(0, option.text().length - 1); + option.val(value).text(value).prop('selected', true); + } + return (keyCode !== 8); + default: + break; + } + }, + + // keypress event handler + // handles the rest of the keys (keypress event gives more informations + // about pressed keys) + keyPress: function (event) { + var keyCode = getKeyCode(event), opt = options[Combobox.getId($(this))], + option, value, specialKeys, exit = false, text; + + Combobox.clearCursor($(this)); + if (keyCode !== 9 && keyCode !== 13 && keyCode !== 27) { + // special keys codes + specialKeys = [37, 38, 39, 40, 46]; + // handle special keys + $.each(specialKeys, function (i, val) { + if (keyCode === val && keyCode === lastKeyCode) { + exit = true; + } + }); + + // don't handle ignored keys + if (!exit && $.inArray(keyCode, opt.ignoredKeys) === -1) { + + // remove selection from all options + $(this).find('option:selected').prop('selected',false); + + if ($.inArray(keyCode, opt.acceptedKeys) !== -1) { + option = $(this).find('option.' + pluginClass).first(); + text = option.text(); + + if (text.length < opt.maxLength) { + value = text + String.fromCharCode(keyCode); + option.val(value).text(value).attr('label',value); + } + + option.prop('selected', true); + } + } + + return false; + } + }, + + keyUp: function (event) { + var opt = options[Combobox.getId($(this))]; + if (opt.triggerChangeEvent) { + $(this).trigger('change'); + } + }, + + // change event handler + // handles editable option changing based on a pre-existing values + change: function () { + var opt = options[Combobox.getId($(this))]; + if (opt.useExistingOptions) { + Combobox.setEditableOption($(this)); + } + }, + + click: function () { + if (!$.browser.safari) { + Combobox.openedState($(this), !Combobox.openedState($(this))); + } + } + }; + }()); + + // Combobox + Combobox = (function () { + var Parameters, EditableOption, generateId, setup; + + // validates and set combobox parameters + Parameters = (function () { + var Set, Remove, Handle; + + Set = (function () { + var parseKeys, Handles; + + parseKeys = function (value) { + var keys = []; + if ($.isArray(value)) { + $.each(value, function (i, val) { + var j, min, max; + if (Validators.keyRange(val)) { + if ($.isArray(val)) { + min = val[0]; + max = val[1]; + } else { + min = val.min; + max = val.max; + } + for (j = min; j <= max; j += 1) { + keys.push(j); + } + } else if (typeof val === 'number' && Validators.integer(val)) { + keys.push(val); + } + }); + } + return keys; + }; + + Handles = (function () { + return { + integer: function (elem, name, value) { + var id = Combobox.getId(elem), opt = options[id]; + if (opt !== undefined && Validators.integer(value)) { + opt[name] = value; + return true; + } + return false; + }, + bool: function (elem, name, value) { + var id = Combobox.getId(elem), opt = options[id]; + if (opt !== undefined && typeof value === 'boolean') { + opt[name] = value; + return true; + } + return false; + }, + array: function (elem, name, value) { + if (typeof value === 'string') { + value = [value]; + } + var id = Combobox.getId(elem), opt = options[id]; + if (opt !== undefined && $.isArray(value)) { + opt[name] = value; + return true; + } + return false; + }, + object: function (elem, name, value) { + var id = Combobox.getId(elem), opt = options[id]; + if (opt !== undefined && value !== null && + typeof value === 'object' && !$.isArray(value)) { + opt[name] = value; + } + }, + keys: function (elem, name, value) { + var id = Combobox.getId(elem), opt = options[id]; + if (opt !== undefined && $.isArray(value)) { + opt[name] = parseKeys(value); + } + } + }; + }()); + + return { + position: function (elem, value) { + if (Handles.integer(elem, 'position', value)) { + var id = Combobox.getId(elem), opt = options[id], optionsCount; + optionsCount = + elem.find('option:not(.' + pluginClass + ')').length; + if (value > optionsCount) { + opt.position = optionsCount; + } + } + }, + + ignoreOptGroups: function (elem, value) { + Handles.bool(elem, 'ignoreOptGroups', value); + }, + + maxLength: function (elem, value) { + if (Handles.integer(elem, 'maxLength', value)) { + var id = Combobox.getId(elem), opt = options[id]; + if (value < 0 || value > 255) { + opt.maxLength = 255; + } + } + }, + + classes: function (elem, value) { + Handles.array(elem, 'classes', value); + }, + + optionClasses: function (elem, value) { + Handles.array(elem, 'optionClasses', value); + }, + + styles: function (elem, value) { + Handles.object(elem, 'styles', value); + }, + + optionStyles: function (elem, value) { + Handles.object(elem, 'optionStyles', value); + }, + + triggerChangeEvent: function (elem, value) { + Handles.bool(elem, 'triggerChangeEvent', value); + }, + + focusOnNewOption: function (elem, value) { + Handles.bool(elem, 'focusOnNewOption', value); + }, + + useExistingOptions: function (elem, value) { + Handles.bool(elem, 'useExistingOptions', value); + }, + + blinkingCursor: function (elem, value) { + Handles.bool(elem, 'blinkingCursor', value); + }, + + blinkingCursorInterval: function (elem, value) { + Handles.integer(elem, 'blinkingCursorInterval', value); + }, + + ignoredKeys: function (elem, value) { + Handles.keys(elem, 'ignoredKeys', value); + }, + + acceptedKeys: function (elem, value) { + Handles.keys(elem, 'acceptedKeys', value); + } + }; + }()); + + Remove = (function () { + var removeClasses, removeStyles; + + removeClasses = function (elem, classes) { + $.each(classes, function (i, val) { + elem.removeClass(val); + }); + }; + + removeStyles = function (elem, styles) { + $.each(styles, function (key, val) { + elem.css(key, ''); + }); + }; + + return { + classes: function (elem) { + var id = Combobox.getId(elem), opt = options[id]; + if (opt !== undefined) { + removeClasses(elem, opt.classes); + } + }, + + optionClasses: function (elem) { + var id = Combobox.getId(elem), opt = options[id]; + if (opt !== undefined) { + removeClasses(elem.find('option.' + pluginClass), + opt.optionClasses); + } + }, + + styles: function (elem) { + var id = Combobox.getId(elem), opt = options[id]; + if (opt !== undefined) { + removeStyles(elem, opt.styles); + } + }, + + optionStyles: function (elem) { + var id = Combobox.getId(elem), opt = options[id]; + if (opt !== undefined) { + removeStyles(elem.find('option.' + pluginClass), + opt.optionStyles); + } + }, + + all: function (elem) { + Remove.classes(elem); + Remove.optionClasses(elem); + Remove.styles(elem); + Remove.optionStyles(elem); + } + }; + }()); + + Handle = (function () { + var setClasses, setStyles; + + setClasses = function (elem, classes) { + $.each(classes, function (i, val) { + elem.addClass(val); + }); + }; + + setStyles = function (elem, styles) { + $.each(styles, function (key, val) { + elem.css(key, val); + }); + }; + + return { + position: function (elem) { + var opt = options[Combobox.getId(elem)], option, uneditableOptions, + container; + option = elem.find('option.' + pluginClass); + + uneditableOptions = elem.find('option:not(.' + pluginClass + ')'); + if (opt.position < uneditableOptions.length) { + container = uneditableOptions.eq(opt.position); + + if (!opt.ignoreOptGroups && + container.parent('optgroup').length > 0) { + uneditableOptions.eq(opt.position).parent().before(option); + } else { + uneditableOptions.eq(opt.position).before(option); + } + } else { + elem.append(option); + } + }, + + classes: function (elem) { + var id = Combobox.getId(elem), opt = options[id]; + if (opt !== undefined) { + setClasses(elem, opt.classes); + } + }, + + optionClasses: function (elem) { + var id = Combobox.getId(elem), opt = options[id]; + if (opt !== undefined) { + setClasses(elem.find('option.' + pluginClass), + opt.optionClasses); + } + }, + + styles: function (elem) { + var id = Combobox.getId(elem), opt = options[id]; + if (opt !== undefined) { + setStyles(elem, opt.styles); + } + }, + + optionStyles: function (elem) { + var id = Combobox.getId(elem), opt = options[id]; + if (opt !== undefined) { + setStyles(elem.find('option.' + pluginClass), + opt.optionStyles); + } + }, + + focusOnNewOption: function (elem) { + var id = Combobox.getId(elem), opt = options[id]; + if (opt !== undefined && opt.focusOnNewOption) { + elem.find('option.' + pluginClass) + .prop('selected', true); + } + }, + + useExistingOptions: function (elem) { + var id = Combobox.getId(elem), opt = options[id]; + if (opt !== undefined && opt.useExistingOptions) { + Combobox.setEditableOption(elem); + } + }, + + all: function (elem) { + Handle.position(elem); + Handle.classes(elem); + Handle.optionClasses(elem); + Handle.styles(elem); + Handle.optionStyles(elem); + Handle.focusOnNewOption(elem); + Handle.useExistingOptions(elem); + } + }; + }()); + + return { + Set: Set, + Remove: Remove, + Handle: Handle + }; + }()); + + EditableOption = (function () { + return { + init: function (elem) { + if (!elem.find('option.' + pluginClass).length) { + var editableOption = $('<option>'); + editableOption.addClass(pluginClass); + elem.append(editableOption); + } + + elem.on('keydown', EventHandlers.keyDown); + elem.on('keypress', EventHandlers.keyPress); + elem.on('keyup', EventHandlers.keyUp); + elem.on('change', EventHandlers.change); + elem.on('focus', EventHandlers.focus); + elem.on('blur', EventHandlers.blur); + elem.on('click', EventHandlers.click); + }, + + destroy: function (elem) { + elem.find('option.' + pluginClass).remove(); + elem.off('keydown', EventHandlers.keyDown); + elem.off('keypress', EventHandlers.keyPress); + elem.off('keyup', EventHandlers.keyUp); + elem.off('change', EventHandlers.change); + elem.off('focus', EventHandlers.focus); + elem.off('blur', EventHandlers.blur); + elem.off('click', EventHandlers.click); + } + }; + }()); + + // generates unique identifier + generateId = function () { + while (true) { + var random = Math.floor(Math.random() * 100000); + + if (options[random] === undefined) { + return random; + } + } + }; + + // sets combobox + setup = function (elem) { + EditableOption.init(elem); + Parameters.Handle.all(elem); + }; + + // Combobox public members + return { + // create editable combobox + init: function (settings) { + return $(this).filter(':uneditable').each(function () { + + var id = generateId(), elem = $(this); + + elem.data('jecId', id); + + // override passed default options + options[id] = $.extend(true, {}, defaults); + + // parse keys + Parameters.Set.ignoredKeys(elem, options[id].ignoredKeys); + Parameters.Set.acceptedKeys(elem, options[id].acceptedKeys); + + if (typeof settings === 'object' && !$.isArray(settings)) { + $.each(settings, function (key, val) { + if (val !== undefined) { + switch (key) { + case 'position': + Parameters.Set.position(elem, val); + break; + case 'ignoreOptGroups': + Parameters.Set.ignoreOptGroups(elem, val); + break; + case 'maxLength': + Parameters.Set.maxLength(elem, val); + break; + case 'classes': + Parameters.Set.classes(elem, val); + break; + case 'optionClasses': + Parameters.Set.optionClasses(elem, val); + break; + case 'styles': + Parameters.Set.styles(elem, val); + break; + case 'optionStyles': + Parameters.Set.optionStyles(elem, val); + break; + case 'triggerChangeEvent': + Parameters.Set.triggerChangeEvent(elem, val); + break; + case 'focusOnNewOption': + Parameters.Set.focusOnNewOption(elem, val); + break; + case 'useExistingOptions': + Parameters.Set.useExistingOptions(elem, val); + break; + case 'blinkingCursor': + Parameters.Set.blinkingCursor(elem, val); + break; + case 'blinkingCursorInterval': + Parameters.Set.blinkingCursorInterval(elem, val); + break; + case 'ignoredKeys': + Parameters.Set.ignoredKeys(elem, val); + break; + case 'acceptedKeys': + Parameters.Set.acceptedKeys(elem, val); + break; + } + } + }); + } + + setup($(this)); + }); + }, + + // creates editable combobox without using existing select elements + initJS: function (options, settings) { + var select, addOptions; + + select = $('<select>'); + + addOptions = function (elem, options) { + if ($.isArray(options)) { + $.each(options, function (i, val) { + if ($.isPlainObject(val)) { + $.each(val, function (key, value) { + if ($.isArray(value)) { + var og = $('<optgroup>').attr('label', key); + addOptions(og, value); + og.appendTo(select); + } else if (typeof value === 'number' || + typeof value === 'string') { + $('<option>').text(value).prop('value', key) + .appendTo(elem); + } + }); + } else if (typeof val === 'string' || typeof val === 'number') { + $('<option>').text(val).prop('value', val).appendTo(elem); + } + }); + } + }; + + addOptions(select, options); + + return select.jec(settings); + }, + + // destroys editable combobox + destroy: function () { + return $(this).filter(':editable').each(function () { + $(this).jecOff(); + $.removeData($(this).get(0), 'jecId'); + $.removeData($(this).get(0), 'jecCursorState'); + $.removeData($(this).get(0), 'jecOpenedState'); + }); + }, + + // enable editablecombobox + enable: function () { + return $(this).filter(':editable').each(function () { + var id = Combobox.getId($(this)), value = values[id]; + + setup($(this)); + + if (value !== undefined) { + $(this).jecValue(value); + } + }); + }, + + // disable editable combobox + disable: function () { + return $(this).filter(':editable').each(function () { + var val = $(this).find('option.' + pluginClass).val(); + values[Combobox.getId($(this))] = val; + Parameters.Remove.all($(this)); + EditableOption.destroy($(this)); + }); + }, + + // gets or sets editable option's value + value: function (value, setFocus) { + if ($(this).filter(':editable').length > 0) { + if (value === null || value === undefined) { + // get value + return $(this).find('option.' + pluginClass).val(); + } else if (typeof value === 'string' || typeof value === 'number') { + // set value + return $(this).filter(':editable').each(function () { + var option = $(this).find('option.' + pluginClass); + option.val(value).text(value); + if (typeof setFocus !== 'boolean' || setFocus) { + option.prop('selected', true); + } + }); + } + } + }, + + // gets or sets editable option's preference + pref: function (name, value) { + if ($(this).filter(':editable').length > 0) { + if (typeof name === 'string') { + if (value === null || value === undefined) { + // get preference + return options[Combobox.getId($(this))][name]; + } else { + // set preference + return $(this).filter(':editable').each(function () { + switch (name) { + case 'position': + Parameters.Set.position($(this), value); + Parameters.Handle.position($(this)); + break; + case 'classes': + Parameters.Remove.classes($(this)); + Parameters.Set.classes($(this), value); + Parameters.Handle.position($(this)); + break; + case 'optionClasses': + Parameters.Remove.optionClasses($(this)); + Parameters.Set.optionClasses($(this), value); + Parameters.Set.optionClasses($(this)); + break; + case 'styles': + Parameters.Remove.styles($(this)); + Parameters.Set.styles($(this), value); + Parameters.Set.styles($(this)); + break; + case 'optionStyles': + Parameters.Remove.optionStyles($(this)); + Parameters.Set.optionStyles($(this), value); + Parameters.Handle.optionStyles($(this)); + break; + case 'focusOnNewOption': + Parameters.Set.focusOnNewOption($(this), value); + Parameters.Handle.focusOnNewOption($(this)); + break; + case 'useExistingOptions': + Parameters.Set.useExistingOptions($(this), value); + Parameters.Handle.useExistingOptions($(this)); + break; + case 'blinkingCursor': + Parameters.Set.blinkingCursor($(this), value); + break; + case 'blinkingCursorInterval': + Parameters.Set.blinkingCursorInterval($(this), value); + break; + case 'ignoredKeys': + Parameters.Set.ignoredKeys($(this), value); + break; + case 'acceptedKeys': + Parameters.Set.acceptedKeys($(this), value); + break; + } + }); + } + } + } + }, + + // sets editable option to the value of currently selected option + setEditableOption: function (elem) { + var value = elem.find('option:selected').text(); + elem.find('option.' + pluginClass).prop('value', elem.val()) + .text(value).prop('selected', true); + }, + + // get combobox id + getId: function (elem) { + return elem.data('jecId'); + }, + + valueIsEditable: function (elem) { + return elem.find('option.' + pluginClass).get(0) === + elem.find('option:selected').get(0); + }, + + clearCursor: function (elem) { + $(elem).find('option.' + cursorClass).each(function () { + var text = $(this).text(); + $(this).removeClass(cursorClass).text(text.substring(0, text.length - 1)); + }); + }, + + cursorState: function (elem, state) { + return elem.data('jecCursorState', state); + }, + + openedState: function (elem, state) { + return elem.data('jecOpenedState', state); + }, + + //handles editable cursor + handleCursor: function () { + if (activeCombobox !== undefined && activeCombobox !== null) { + if ($.browser.msie && Combobox.openedState(activeCombobox)) { + return; + } + + var state = Combobox.cursorState(activeCombobox), elem; + if (state) { + Combobox.clearCursor(activeCombobox); + } else if (Combobox.valueIsEditable(activeCombobox)) { + elem = activeCombobox.find('option:selected'); + elem.addClass(cursorClass).text(elem.text() + '|'); + } + Combobox.cursorState(activeCombobox, !state); + } + } + }; + }()); + + // jEC public members + return { + init: Combobox.init, + enable: Combobox.enable, + disable: Combobox.disable, + destroy: Combobox.destroy, + value: Combobox.value, + pref: Combobox.pref, + initJS: Combobox.initJS, + handleCursor: Combobox.handleCursor + }; + }()); + + // register functions + $.fn.extend({ + jec: $.jEC.init, + jecOn: $.jEC.enable, + jecOff: $.jEC.disable, + jecKill: $.jEC.destroy, + jecValue: $.jEC.value, + jecPref: $.jEC.pref + }); + + $.extend({ + jec: $.jEC.initJS + }); + + // register selectors + $.extend($.expr[':'], { + editable: function (a) { + var data = $(a).data('jecId'); + return data !== null && data !== undefined; + }, + + uneditable: function (a) { + var data = $(a).data('jecId'); + return data === null || data === undefined; + } + }); + }(jQuery));
\ No newline at end of file diff --git a/js/jquery.scrollTo-min.js b/js/jquery.scrollTo-min.js index 73a3341..5e78778 100644 --- a/js/jquery.scrollTo-min.js +++ b/js/jquery.scrollTo-min.js @@ -1,11 +1,11 @@ -/**
- * jQuery.ScrollTo - Easy element scrolling using jQuery.
- * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
- * Dual licensed under MIT and GPL.
- * Date: 5/25/2009
- * @author Ariel Flesler
- * @version 1.4.2
- *
- * http://flesler.blogspot.com/2007/10/jqueryscrollto.html
- */
+/** + * jQuery.ScrollTo - Easy element scrolling using jQuery. + * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com + * Dual licensed under MIT and GPL. + * Date: 5/25/2009 + * @author Ariel Flesler + * @version 1.4.2 + * + * http://flesler.blogspot.com/2007/10/jqueryscrollto.html + */ ;(function(d){var k=d.scrollTo=function(a,i,e){d(window).scrollTo(a,i,e)};k.defaults={axis:'xy',duration:parseFloat(d.fn.jquery)>=1.3?0:1};k.window=function(a){return d(window)._scrollable()};d.fn._scrollable=function(){return this.map(function(){var a=this,i=!a.nodeName||d.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!i)return a;var e=(a.contentWindow||a).document||a.ownerDocument||a;return d.browser.safari||e.compatMode=='BackCompat'?e.body:e.documentElement})};d.fn.scrollTo=function(n,j,b){if(typeof j=='object'){b=j;j=0}if(typeof b=='function')b={onAfter:b};if(n=='max')n=9e9;b=d.extend({},k.defaults,b);j=j||b.speed||b.duration;b.queue=b.queue&&b.axis.length>1;if(b.queue)j/=2;b.offset=p(b.offset);b.over=p(b.over);return this._scrollable().each(function(){var q=this,r=d(q),f=n,s,g={},u=r.is('html,body');switch(typeof f){case'number':case'string':if(/^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(f)){f=p(f);break}f=d(f,this);case'object':if(f.is||f.style)s=(f=d(f)).offset()}d.each(b.axis.split(''),function(a,i){var e=i=='x'?'Left':'Top',h=e.toLowerCase(),c='scroll'+e,l=q[c],m=k.max(q,i);if(s){g[c]=s[h]+(u?0:l-r.offset()[h]);if(b.margin){g[c]-=parseInt(f.css('margin'+e))||0;g[c]-=parseInt(f.css('border'+e+'Width'))||0}g[c]+=b.offset[h]||0;if(b.over[h])g[c]+=f[i=='x'?'width':'height']()*b.over[h]}else{var o=f[h];g[c]=o.slice&&o.slice(-1)=='%'?parseFloat(o)/100*m:o}if(/^\d+$/.test(g[c]))g[c]=g[c]<=0?0:Math.min(g[c],m);if(!a&&b.queue){if(l!=g[c])t(b.onAfterFirst);delete g[c]}});t(b.onAfter);function t(a){r.animate(g,j,b.easing,a&&function(){a.call(this,n,b)})}}).end()};k.max=function(a,i){var e=i=='x'?'Width':'Height',h='scroll'+e;if(!d(a).is('html,body'))return a[h]-d(a)[e.toLowerCase()]();var c='client'+e,l=a.ownerDocument.documentElement,m=a.ownerDocument.body;return Math.max(l[h],m[h])-Math.min(l[c],m[c])};function p(a){return typeof a=='object'?a:{top:a,left:a}}})(jQuery);
\ No newline at end of file diff --git a/js/utils.js b/js/utils.js index 3adfca7..ec49c3a 100644 --- a/js/utils.js +++ b/js/utils.js @@ -1,1376 +1,1376 @@ -/**
- * @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:'<div align="center">HTTP error: ' + d.status + ' ' + d.statusText+"</div>",details:''};
- for(var i in d) {
- if(typeof(d[i]) == 'function' || typeof(d[i]) == 'object') continue;
- err.details += i + ': "' + d[i] + '"' + "\n";
- }
- phpVirtualBoxFailure(err);
-
- } else {
- phpVirtualBoxFailure('<div align="center">(General communication failure)');
- }
- }
-
- return null;
-
- // Filter out data and display error messages
- }).pipe(function(d){
-
- // Fatal error previously occurred
- if($('#vboxPane').data('vboxFatalError')) {
- return null;
- }
-
- // Append debug output to console
- if(d && d.messages && window.console && window.console.log) {
- for(var i = 0; i < d.messages.length; i++) {
- window.console.log(d.messages[i]);
- }
- }
-
- if(d.errors.length > 0) {
-
-
- for(var i = 0; i < d.errors.length; i++) {
-
- // Handle fatal and connection errors
- if(d.errors[i].fatal || d.errors[i].connection) {
-
- // Multiple Servers check
- if(d.errors[i].connection && $('#vboxPane').data('vboxConfig') ) {
-
- $('#vboxPane').data('vboxFatalError',1);
- $('#vboxPane').css({'display':'none'});
-
- s='';
- if($('#vboxPane').data('vboxConfig').servers && $('#vboxPane').data('vboxConfig').servers.length) {
- var servers = $('#vboxPane').data('vboxConfig').servers;
- for(var a = 0; a < servers.length; a++) {
- servers[a] = "<a href='?server="+servers[a].name+"'>"+$('<div />').html(servers[a].name).text()+"</a>";
- }
- s = '<div style="display: block">'+trans('Server List','phpVirtualBox')+': '+servers.join(', ')+'</div>';
- }
- if(s) vboxAlert(s);
- vboxAlert(d.errors[i],{'width':'400px'});
- 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%'});
-
-
-
- // 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 = $('<div />').attr({'id':'vboxBrowseFolder','class':'vboxDialogContent','style':'display:none'});
-
- $('<div />').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':'<img src="'+(icon ? icon : 'images/jqueryFileTree/'+(foldersonly ? 'folder_open' : 'file')+'.png') + '" class="vboxDialogTitleIcon" /> ' + (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 to <p>
- if(msg[0] != '<') msg = '<p>'+msg+'</p>';
-
- 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);
-
-
- if(typeof e == 'object' && e.details) {
-
- // Details can contain HTML entities
- e.details = $('<div />').html(e.details).text();
-
- var p = $('<p />').attr({'style':'text-align: center'});
- $('<a />').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 = $('<div />').attr({'style':'display: none; height: 100%; width: auto;','class':'vboxAlert'});
- $('<textarea />').attr({'spellcheck':'false','wrap':'off','readonly':'true'}).val(e.details).appendTo($('<form />').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':'<img src="images/vbox/OSE/about_16px.png" class="vboxDialogTitleIcon" /> 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 = $('<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);
-
- 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':'<img src="images/vbox/OSE/about_16px.png" class="vboxDialogTitleIcon" /> 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($('<div />').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 = $('<tr />');
-
- for(var a = 0; a < diff; a++) {
- $(tr).append($('<td />').attr({'style':'width: '+ tdw + '%'}));
- }
- $('<table />').attr({'class':'sliderScale'}).append(tr).appendTo(this);
-
- });
-
- // save value
- $(this).slider('value',$(this).slider('value'));
-
- // Min / Max labels
- if(!$(this).data('noMinMaxLabels')) {
- var min = $(this).slider('option','min');
- var max = $(this).slider('option','max');
- $(this).closest('table').find('.vboxSliderMin').html(function(i,h){return ' ' + trans(h,context,min,$(this).attr('title')).replace('%1',min);});
- $(this).closest('table').find('.vboxSliderMax').html(function(i,h){return ' ' + trans(h,context,max,$(this).attr('title')).replace('%1',max);});
- }
- });
-
-
- /*
- * Translations
- */
- $(root).find(".translate").html(function(i,h){return trans($('<div />').html(h).text(),context);}).removeClass('translate');
-
- /*
- * Setup Tabs
- */
- $(root).find(".vboxTabbed").tabs();
-
-
- /* Image buttons */
- if(!jQuery.browser.msie) {
-
- $(root).find('input.vboxImgButton').on('mousedown',function(){
-
- var xy = $(this).css('backgroundPosition').split(' ');
-
- if(!$(this).data('startX')) $(this).data('startX', parseInt(xy[0]));
- if(!$(this).data('startY')) $(this).data('startY', parseInt(xy[1]));
-
- $(this).css('backgroundPosition',(parseInt($(this).data('startX'))+1)+'px '+(parseInt($(this).data('startY'))+1)+'px');
-
- var btn = this;
- $(document).one('mouseup',function(){
- $(btn).css('backgroundPosition',$(btn).data('startX')+'px '+$(btn).data('startY')+'px');
- });
-
- });
-
- }
-
- /*
- *
- * Enable / disable sections (Remote Display, Audio, Network Adapters, usb)
- *
- */
-
- $(root).find('input.vboxEnablerCheckbox').on('click', function(e) {
-
- var roottbl = $(this).closest('table');
-
- $(roottbl).find('input:not(.vboxEnablerCheckbox)').prop('disabled',!this.checked);
- $(roottbl).find('select:not(.vboxEnablerIgnore)').prop('disabled',!this.checked);
- (this.checked ? $(roottbl).find('th').removeClass('vboxDisabled') : $(roottbl).find('th:not(.vboxEnablerIgnore)').addClass('vboxDisabled'));
- (this.checked ? $(roottbl).find('.vboxEnablerListen').removeClass('vboxDisabled') : $(roottbl).find('.vboxEnablerListen').addClass('vboxDisabled'));
-
- // Find any enabler / disabler listeners
- $(roottbl).find('.vboxEnablerTrigger').trigger(this.checked ? 'enable' : 'disable');
-
-
- });
-
-
- /*
- * Tooltips
- */
- $(root).find('.vboxToolbarSmallButton').tipped({'source':'title','mode':'hover'});
-
-
- /*
- * File / Folder browsers
- */
- if($('#vboxPane').data('vboxConfig').browserDisable) {
- $(root).find('table td.vboxFileFolderInput input.vboxImgButton').hide();
- }
-
-
-}
-
-/**
- * Color VISIBLE children rows of parent elm
- * @param {HTMLNode} elm - element who's children to color
- * @param {Boolean} startOdd - start on the 2nd child (optional)
- * @param {String} headerClass - if child node has headerClass class, consider it a header and skip coloring (optional)
- */
-function vboxColorRows(elm,startOdd,headerClass) {
- var odd = 0;
- if(startOdd) odd = 1;
- $(elm).children().each(function(i){
- if(headerClass && $(this).hasClass(headerClass)) {
- odd = (startOdd ? 1 : 0);
- return;
- }
- if($(this).css('display') == 'none' || $(this).hasClass('vboxListItemDisabled')) return;
- (odd++ % 2 ? $(this).addClass('vboxOddRow') : $(this).removeClass('vboxOddRow'));
- });
-}
-
-/**
- * Return an HTML div node sized to parent with overflow hidden
- * @param {HTMLNode} p - node to add div to
- * @return {HTMLNode}
- */
-function vboxDivOverflowHidden(p) {
- var w = $(p).innerWidth();
- w -= parseInt($(p).css('padding-right'));
- w -= parseInt($(p).css('padding-left'));
- return $('<div />').css({'width':(w-4)+'px','overflow':'hidden','padding':'0px','margin':'0px','border':'0px'});
-}
-
-
-/**
- * Show progress dialog and periodically poll the progress' status
- *
- * @param {String} prequest - request object passed to ajax
- * @param {Function} callback - function to run on progress completion
- * @param {String} icon - URL of image to display on progress operation dialog (optional)
- * @param {String} title - title of progress operation dialog (optional)
- * @param {String} target - contextual target of progress operation
- * @param {Boolean} blocking - true if progress operation should block other ops
- * @see vboxconnector::progressGet()
- */
-function vboxProgress(prequest,callback,icon,title,target,blocking) {
-
- // Fix title
- title = title.replace('\.+$','');
-
- // Sanitize target
- target = $('<div />').text(target).html();
-
- // Sanitize progress request data
- var persist = prequest.persist;
- prequest = {
- 'progress' : prequest.progress,
- 'catcherrs' : prequest.catcherrs
- };
-
- // Blocking creates a dialog
- if(!blocking) {
-
- vboxProgressCreateListElement(prequest,icon,title,target,callback);
-
- $.when(prequest, vboxAjaxRequest('progressGet',prequest,{'persist':persist})).done(vboxProgressUpdate);
-
- } else {
-
- vboxProgressCreateDialog(prequest,icon,title,target,callback);
-
- $.when(prequest, vboxAjaxRequest('progressGet',prequest,{'persist':persist})).done(vboxProgressUpdateModal);
- }
-
-
-
-}
-
-/**
- * Generate modal progress dialog
- *
- * @param {Object} prequest - progress operation request object
- * @param {String} icon - URL of image to display on progress operation dialog (optional)
- * @param {String} title - title of progress operation dialog (optional)
- * @param {String} target - contextual target of progress operation
- * @param {Function} callback - function to run on progress completion
- * @see vboxconnector::progressGet()
- */
-function vboxProgressCreateDialog(prequest,icon,title,target,callback) {
-
- // Shorthand
- var pid = prequest.progress;
-
- var div = $('<div />').attr({'id':'vboxProgress'+pid,'title':(title ? title : 'phpVirtualBox'),'style':'text-align: center'});
-
- var tbl = $('<table />').css({'width':'100%'});
- var tr = $('<tr />').css({'vertical-align':'middle'});
- var td = $('<td />').css({'padding':'0px','text-align':'left','width':'1px'});
- if(icon) {
- $('<img />').css({'margin':'4px'}).attr({'src':'images/vbox/'+icon,'height':'90','width':'90'}).appendTo(td);
- }
- $(tr).append(td);
-
- var td = $('<td />').css({'text-align':'center','padding':'4px'}).append($('<div />').attr({'id':'vboxProgressBar'+pid,'margin':'4px'}).progressbar({ value: 1 }));
-
- $('<div />').attr({'id':'vboxProgressText'+pid}).html('<img src="images/spinner.gif" />').appendTo(td);
-
- // Cancel button
- $('<div />').attr({'id':'vboxProgressCancel'+pid}).css({'display':'none','padding':'8px'}).append(
-
- $('<input />').attr('type','button').val(trans('Cancel','QIMessageBox')).data({'pid':pid}).click(function(){
- this.disabled = 'disabled';
- vboxAjaxRequest('progressCancel',prequest);
- })
- ).appendTo(td);
-
-
- $(tbl).append($(tr).append(td)).appendTo(div);
-
- // Append placeholder for list element
- $('#vboxProgressOps').prepend($('<div />').addClass('vboxProgressOpElement').css({'display':'none'}).attr({'id':'vboxProgressPlaceholder'+pid}));
-
- $(div).data({
- 'vboxCallback':callback,
- 'vboxIcon' : icon,
- 'vboxTitle' : title,
- 'vboxTarget' : target
- }).dialog({'width':400,'height':'auto','closeOnEscape':false,'modal':true,'resizable':false,'draggable':true,'closeOnEscape':false,'buttons':{}});
-
-
-}
-
-/**
- * Generate progress list element and append it
- *
- * @param {Object} prequest - progress operation request object
- * @param {String} icon - URL of image to display on progress operation dialog (optional)
- * @param {String} title - title of progress operation dialog (optional)
- * @param {String} target - contextual target of progress operation
- * @param {Function} callback - function to run on progress completion
- * @see vboxconnector::progressGet()
- */
-function vboxProgressCreateListElement(prequest,icon,title,target,callback) {
-
- // Shorthand
- var pid = prequest.progress;
-
- var div = $('<div />').attr({'id':'vboxProgress'+pid}).addClass('vboxProgressOpElement');
-
- var divOpTitle = $('<div />').addClass('vboxProgressOpTitle');
-
- if(icon) {
- $('<img />').attr({'src':'images/vbox/'+icon,'height':'16','width':'16'}).appendTo(divOpTitle);
- }
-
- // Title
- if($('#vboxPane').data('vboxConfig').servers.length) {
- title = $('#vboxPane').data('vboxConfig').name + ': ' + title;
- }
-
- $(divOpTitle).append(title + (target ? ' (' + target + ')' : '')).appendTo(div);
-
- // Progress bar
- $('<div />').addClass('vboxProgressBarContainer').append(
- $('<div />').attr({'id':'vboxProgressBar'+pid}).progressbar({ value: 1 })
- ).appendTo(div);
-
- // Progress text
- $('<div />').addClass('vboxProgressOpText').append(
- $('<span />').attr({'id':'vboxProgressText'+pid}).html('<img src="images/spinner.gif" height=12 width=12/>')
- ).appendTo(div);
-
- // Cancel button
- $('<div />').addClass('vboxProgressOpCancel').append(
- $('<input />').attr({'id':'vboxProgressCancel'+pid,'type':'button'}).val(trans('Cancel','UIProgressDialog')).data({'pid':pid})
- .click(function(){
- this.disabled = 'disabled';
- vboxAjaxRequest('progressCancel',prequest);
- })
- .css({'margin':'0px'})
- ).appendTo(div);
-
- $(div).data({'vboxCallback':callback})
-
- if($('#vboxProgressPlaceholder'+pid)[0]) {
- $('#vboxProgressPlaceholder'+pid).replaceWith(div);
- } else {
- $('#vboxProgressOps').prepend(div);
- }
-
-
-}
-
-/**
- * OnUnload warning shown when an operation is in progress
- * @return {String} warning message indicating operation is in progress
- */
-function vboxOpInProgressCheck() {
- if($('#vboxProgressOps').children('div.vboxProgressOpElement:not(.vboxProgressComplete)').addClass('vboxProgressRunning').length) {
- 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');
- }
-}
-
-/**
- * Update progress dialog box. Callback run from vboxAjaxRequest
- *
- * @param {Object} prequest - progress operation data passed to ajax call
- * @param {Object} data - data returned from progressGet AJAX call
- */
-function vboxProgressUpdateModal(prequest, data) {
- vboxProgressUpdate(prequest,data,true);
-}
-
-/**
- * Update progress dialog box or progress list row with % completed
- *
- * @param {Object} prequest - progress operation data passed to ajax call
- * @param {Object} d - data returned from progressGet AJAX call
- * @param {Boolean} modal - true if updating modal dialog
- * @see vboxconnector::progressGet()
- */
-function vboxProgressUpdate(prequest,d,modal) {
-
- // Shorthand
- var pid = prequest.progress;
-
- // check for completed progress
- if(!d || !d.responseData || !d.responseData['progress'] || !d.responseData['info'] || d.responseData['info']['completed'] || d.responseData['info']['canceled']) {
-
- if(d && d.responseData['info'] && d.responseData['info']['canceled'])
- vboxAlert(trans('Operation Canceled','phpVirtualBox'),{'width':'300px','height':'auto'});
-
- var callback = $("#vboxProgress"+pid).data('vboxCallback');
-
- $("#vboxProgressBar"+pid).progressbar({ value: 100 });
-
- if(modal) {
-
- var icon = $("#vboxProgress"+pid).data('vboxIcon');
- var title = $("#vboxProgress"+pid).data('vboxTitle');
- var target = $("#vboxProgress"+pid).data('vboxTarget');
-
- $("#vboxProgress"+pid).empty().remove();
-
- if(callback) callback(d);
-
- // Now append to list
- vboxProgressCreateListElement(prequest,icon,title,target);
- vboxProgressUpdate(prequest);
-
- } else {
-
- var sdate = new Date();
- $("#vboxProgressText"+pid).html(sdate.toLocaleString());
- $('#vboxProgressCancel'+pid).remove();
-
- if(callback) callback(d);
- }
-
- $("#vboxProgress"+pid).addClass('vboxProgressComplete').removeClass('vboxProgressRunning');
-
- // Remove data
- $("#vboxProgress"+pid).removeData([
- 'vboxCallback',
- 'vboxIcon',
- 'vboxTitle',
- 'vboxTarget'
- ]);
-
- // Check for max elements
- if($('#vboxPane').data('vboxConfig').maxProgressList) {
- var maxList = $('#vboxPane').data('vboxConfig').maxProgressList;
- try {
- maxList = Math.max(2,parseInt(maxList));
- } catch (e) {
- maxList = 5;
- }
- if(maxList > 0) $('#vboxProgressOps').children('div.vboxProgressComplete').slice(maxList).remove();
-
- }
-
- return;
- }
-
- // update percent
- $("#vboxProgressBar"+pid).progressbar({ value: d.responseData.info.percent });
- $("#vboxProgressText"+pid).html(d.responseData.info.percent+'%'+(modal ? '<br />' : ' ') + d.responseData.info.operationDescription);
-
- // Cancelable?
- if(d.responseData.info.cancelable) {
- $('#vboxProgressCancel'+pid).show();
- }
-
- // Get request
- var def = $.Deferred();
- def.done(function(){
-
- $.when(prequest, vboxAjaxRequest('progressGet', prequest, {'persist': d.persist}))
- .done((modal ? vboxProgressUpdateModal : vboxProgressUpdate));
-
- });
- window.setTimeout(def.resolve, 2000);
-
-}
-
-/**
- * Position element to mouse event
- * @param {HTMLNode} elm - HTML node to position
- * @param {Event} e - Event to position to
- */
-function vboxPositionEvent(elm,e) {
-
- var d = {};
-
- if( self.innerHeight ) {
- d.pageYOffset = self.pageYOffset;
- d.pageXOffset = self.pageXOffset;
- d.innerHeight = self.innerHeight;
- d.innerWidth = self.innerWidth;
- } else if( document.documentElement &&
- document.documentElement.clientHeight ) {
- d.pageYOffset = document.documentElement.scrollTop;
- d.pageXOffset = document.documentElement.scrollLeft;
- d.innerHeight = document.documentElement.clientHeight;
- d.innerWidth = document.documentElement.clientWidth;
- } else if( document.body ) {
- d.pageYOffset = document.body.scrollTop;
- d.pageXOffset = document.body.scrollLeft;
- d.innerHeight = document.body.clientHeight;
- d.innerWidth = document.body.clientWidth;
- }
-
- $(elm).css({'left':0,'top':0});
-
- (e.pageX) ? x = e.pageX : x = e.clientX + d.scrollLeft;
- (e.pageY) ? y = e.pageY : y = e.clientY + d.scrollTop;
-
- //adjust to ensure element is inside viewable screen
- var right = x + $(elm).outerWidth();
- var bottom = y + $(elm).outerHeight();
-
- var windowWidth = $(window).width() + $(window).scrollLeft()-5;
- var windowHeight = $(window).height() + $(window).scrollTop()-5;
-
- x = (right > windowWidth) ? x - (right - windowWidth) : x;
- y = (bottom > windowHeight) ? y - (bottom - windowHeight) : y;
-
- $(elm).css({ top: y, left: x });
-}
-
-/**
- * Position element inside visible window
- * @param {HTMLNode} elm - element
- */
-function vboxPositionToWindow(elm) {
-
- var offset = $(elm).offset();
- var x = offset.left;
- var y = offset.top;
-
- //adjust to ensure menu is inside viewable screen
- var right = x + $(elm).outerWidth();
- var bottom = y + $(elm).outerHeight();
-
- var windowWidth = $(window).width() + $(window).scrollLeft();
- var windowHeight = $(window).height() + $(window).scrollTop();
-
- x = (right > windowWidth) ? x - (right - windowWidth) : x;
- y = (bottom > windowHeight) ? y - (bottom - windowHeight) : y;
-
- $(elm).css({'top':y,'left':x});
-
-}
-
-/*
- * keycode input validation functions
- */
-/**
- * Return true if k param is a number
- * @param {Integer} k - keycode
- * @return {Boolean}
- */
-function vboxValidateNum(k) {
- return ((k >= 96 && k <= 105)||(k >= 48 && k <= 57));
-}
-/**
- * Return true if k param is a number or '.'
- * @param {Integer} k - keycode
- * @return {Boolean}
- */
-function vboxValidateIP(k) {
- return (vboxValidateNum(k) || k == 190 || k == 110 || k == 59 || k==78);
-}
-/**
- * Return true if k param is a valid control code (shift, backspace, etc..)
- * @param {Integer} k - keycode
- * @return {Boolean}
- */
-function vboxValidateCtrl(k) {
- switch(k) {
- case 8: // backspace
- case 37: // left | right
- case 39:
- case 27: // esc
- case 16: // shift
- case 17: // ctrl
- case 35: // end
- case 36: // home
- case 46: // del
- case 144: // numlock
- case 20: // capslock
- case 18: // alt
- return true;
- }
- return false;
-}
-
-/** Parse Cookies and populate $('#vboxPane').data('vboxCookies') */
-function vboxParseCookies() {
- if($('#vboxPane').data('vboxCookiesParsed')) return;
- var cookies = {};
- var c = document.cookie.split('; ');
- for(var i = 0; i < c.length; i++) {
- var nv = c[i].split('=');
- cookies[nv[0]] = nv[1];
- }
- $('#vboxPane').data('vboxCookies', cookies);
- $('#vboxPane').data('vboxCookiesParsed',true);
-}
-
-/**
- * General application failure
- * @param {String|Object} msg - Optional extra message appended to error
- * or error object passed to vboxAlert
- */
-function phpVirtualBoxFailure(msg) {
- if($('#vboxPane').data('vboxFatalError')) return;
- $('#vboxPane').data('vboxFatalError', 1);
- $('#vboxPane').css({'display':'none'});
- $('#vboxPane').trigger('phpVirtualBoxFailure');
- if(typeof(msg) == 'string') {
- 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 : ''));
- } 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.<p>The list of virtual machines will not begin auto-refreshing again until this page is reloaded.</p>','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;
-
-});
+/** + * @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:'<div align="center">HTTP error: ' + d.status + ' ' + d.statusText+"</div>",details:''}; + for(var i in d) { + if(typeof(d[i]) == 'function' || typeof(d[i]) == 'object') continue; + err.details += i + ': "' + d[i] + '"' + "\n"; + } + phpVirtualBoxFailure(err); + + } else { + phpVirtualBoxFailure('<div align="center">(General communication failure)'); + } + } + + return null; + + // Filter out data and display error messages + }).pipe(function(d){ + + // Fatal error previously occurred + if($('#vboxPane').data('vboxFatalError')) { + return null; + } + + // Append debug output to console + if(d && d.messages && window.console && window.console.log) { + for(var i = 0; i < d.messages.length; i++) { + window.console.log(d.messages[i]); + } + } + + if(d.errors.length > 0) { + + + for(var i = 0; i < d.errors.length; i++) { + + // Handle fatal and connection errors + if(d.errors[i].fatal || d.errors[i].connection) { + + // Multiple Servers check + if(d.errors[i].connection && $('#vboxPane').data('vboxConfig') ) { + + $('#vboxPane').data('vboxFatalError',1); + $('#vboxPane').css({'display':'none'}); + + s=''; + if($('#vboxPane').data('vboxConfig').servers && $('#vboxPane').data('vboxConfig').servers.length) { + var servers = $('#vboxPane').data('vboxConfig').servers; + for(var a = 0; a < servers.length; a++) { + servers[a] = "<a href='?server="+servers[a].name+"'>"+$('<div />').html(servers[a].name).text()+"</a>"; + } + s = '<div style="display: block">'+trans('Server List','phpVirtualBox')+': '+servers.join(', ')+'</div>'; + } + if(s) vboxAlert(s); + vboxAlert(d.errors[i],{'width':'400px'}); + 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%'}); + + + + // 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 = $('<div />').attr({'id':'vboxBrowseFolder','class':'vboxDialogContent','style':'display:none'}); + + $('<div />').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':'<img src="'+(icon ? icon : 'images/jqueryFileTree/'+(foldersonly ? 'folder_open' : 'file')+'.png') + '" class="vboxDialogTitleIcon" /> ' + (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 to <p> + if(msg[0] != '<') msg = '<p>'+msg+'</p>'; + + 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); + + + if(typeof e == 'object' && e.details) { + + // Details can contain HTML entities + e.details = $('<div />').html(e.details).text(); + + var p = $('<p />').attr({'style':'text-align: center'}); + $('<a />').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 = $('<div />').attr({'style':'display: none; height: 100%; width: auto;','class':'vboxAlert'}); + $('<textarea />').attr({'spellcheck':'false','wrap':'off','readonly':'true'}).val(e.details).appendTo($('<form />').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':'<img src="images/vbox/OSE/about_16px.png" class="vboxDialogTitleIcon" /> 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 = $('<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); + + 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':'<img src="images/vbox/OSE/about_16px.png" class="vboxDialogTitleIcon" /> 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($('<div />').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 = $('<tr />'); + + for(var a = 0; a < diff; a++) { + $(tr).append($('<td />').attr({'style':'width: '+ tdw + '%'})); + } + $('<table />').attr({'class':'sliderScale'}).append(tr).appendTo(this); + + }); + + // save value + $(this).slider('value',$(this).slider('value')); + + // Min / Max labels + if(!$(this).data('noMinMaxLabels')) { + var min = $(this).slider('option','min'); + var max = $(this).slider('option','max'); + $(this).closest('table').find('.vboxSliderMin').html(function(i,h){return ' ' + trans(h,context,min,$(this).attr('title')).replace('%1',min);}); + $(this).closest('table').find('.vboxSliderMax').html(function(i,h){return ' ' + trans(h,context,max,$(this).attr('title')).replace('%1',max);}); + } + }); + + + /* + * Translations + */ + $(root).find(".translate").html(function(i,h){return trans($('<div />').html(h).text(),context);}).removeClass('translate'); + + /* + * Setup Tabs + */ + $(root).find(".vboxTabbed").tabs(); + + + /* Image buttons */ + if(!jQuery.browser.msie) { + + $(root).find('input.vboxImgButton').on('mousedown',function(){ + + var xy = $(this).css('backgroundPosition').split(' '); + + if(!$(this).data('startX')) $(this).data('startX', parseInt(xy[0])); + if(!$(this).data('startY')) $(this).data('startY', parseInt(xy[1])); + + $(this).css('backgroundPosition',(parseInt($(this).data('startX'))+1)+'px '+(parseInt($(this).data('startY'))+1)+'px'); + + var btn = this; + $(document).one('mouseup',function(){ + $(btn).css('backgroundPosition',$(btn).data('startX')+'px '+$(btn).data('startY')+'px'); + }); + + }); + + } + + /* + * + * Enable / disable sections (Remote Display, Audio, Network Adapters, usb) + * + */ + + $(root).find('input.vboxEnablerCheckbox').on('click', function(e) { + + var roottbl = $(this).closest('table'); + + $(roottbl).find('input:not(.vboxEnablerCheckbox)').prop('disabled',!this.checked); + $(roottbl).find('select:not(.vboxEnablerIgnore)').prop('disabled',!this.checked); + (this.checked ? $(roottbl).find('th').removeClass('vboxDisabled') : $(roottbl).find('th:not(.vboxEnablerIgnore)').addClass('vboxDisabled')); + (this.checked ? $(roottbl).find('.vboxEnablerListen').removeClass('vboxDisabled') : $(roottbl).find('.vboxEnablerListen').addClass('vboxDisabled')); + + // Find any enabler / disabler listeners + $(roottbl).find('.vboxEnablerTrigger').trigger(this.checked ? 'enable' : 'disable'); + + + }); + + + /* + * Tooltips + */ + $(root).find('.vboxToolbarSmallButton').tipped({'source':'title','mode':'hover'}); + + + /* + * File / Folder browsers + */ + if($('#vboxPane').data('vboxConfig').browserDisable) { + $(root).find('table td.vboxFileFolderInput input.vboxImgButton').hide(); + } + + +} + +/** + * Color VISIBLE children rows of parent elm + * @param {HTMLNode} elm - element who's children to color + * @param {Boolean} startOdd - start on the 2nd child (optional) + * @param {String} headerClass - if child node has headerClass class, consider it a header and skip coloring (optional) + */ +function vboxColorRows(elm,startOdd,headerClass) { + var odd = 0; + if(startOdd) odd = 1; + $(elm).children().each(function(i){ + if(headerClass && $(this).hasClass(headerClass)) { + odd = (startOdd ? 1 : 0); + return; + } + if($(this).css('display') == 'none' || $(this).hasClass('vboxListItemDisabled')) return; + (odd++ % 2 ? $(this).addClass('vboxOddRow') : $(this).removeClass('vboxOddRow')); + }); +} + +/** + * Return an HTML div node sized to parent with overflow hidden + * @param {HTMLNode} p - node to add div to + * @return {HTMLNode} + */ +function vboxDivOverflowHidden(p) { + var w = $(p).innerWidth(); + w -= parseInt($(p).css('padding-right')); + w -= parseInt($(p).css('padding-left')); + return $('<div />').css({'width':(w-4)+'px','overflow':'hidden','padding':'0px','margin':'0px','border':'0px'}); +} + + +/** + * Show progress dialog and periodically poll the progress' status + * + * @param {String} prequest - request object passed to ajax + * @param {Function} callback - function to run on progress completion + * @param {String} icon - URL of image to display on progress operation dialog (optional) + * @param {String} title - title of progress operation dialog (optional) + * @param {String} target - contextual target of progress operation + * @param {Boolean} blocking - true if progress operation should block other ops + * @see vboxconnector::progressGet() + */ +function vboxProgress(prequest,callback,icon,title,target,blocking) { + + // Fix title + title = title.replace('\.+$',''); + + // Sanitize target + target = $('<div />').text(target).html(); + + // Sanitize progress request data + var persist = prequest.persist; + prequest = { + 'progress' : prequest.progress, + 'catcherrs' : prequest.catcherrs + }; + + // Blocking creates a dialog + if(!blocking) { + + vboxProgressCreateListElement(prequest,icon,title,target,callback); + + $.when(prequest, vboxAjaxRequest('progressGet',prequest,{'persist':persist})).done(vboxProgressUpdate); + + } else { + + vboxProgressCreateDialog(prequest,icon,title,target,callback); + + $.when(prequest, vboxAjaxRequest('progressGet',prequest,{'persist':persist})).done(vboxProgressUpdateModal); + } + + + +} + +/** + * Generate modal progress dialog + * + * @param {Object} prequest - progress operation request object + * @param {String} icon - URL of image to display on progress operation dialog (optional) + * @param {String} title - title of progress operation dialog (optional) + * @param {String} target - contextual target of progress operation + * @param {Function} callback - function to run on progress completion + * @see vboxconnector::progressGet() + */ +function vboxProgressCreateDialog(prequest,icon,title,target,callback) { + + // Shorthand + var pid = prequest.progress; + + var div = $('<div />').attr({'id':'vboxProgress'+pid,'title':(title ? title : 'phpVirtualBox'),'style':'text-align: center'}); + + var tbl = $('<table />').css({'width':'100%'}); + var tr = $('<tr />').css({'vertical-align':'middle'}); + var td = $('<td />').css({'padding':'0px','text-align':'left','width':'1px'}); + if(icon) { + $('<img />').css({'margin':'4px'}).attr({'src':'images/vbox/'+icon,'height':'90','width':'90'}).appendTo(td); + } + $(tr).append(td); + + var td = $('<td />').css({'text-align':'center','padding':'4px'}).append($('<div />').attr({'id':'vboxProgressBar'+pid,'margin':'4px'}).progressbar({ value: 1 })); + + $('<div />').attr({'id':'vboxProgressText'+pid}).html('<img src="images/spinner.gif" />').appendTo(td); + + // Cancel button + $('<div />').attr({'id':'vboxProgressCancel'+pid}).css({'display':'none','padding':'8px'}).append( + + $('<input />').attr('type','button').val(trans('Cancel','QIMessageBox')).data({'pid':pid}).click(function(){ + this.disabled = 'disabled'; + vboxAjaxRequest('progressCancel',prequest); + }) + ).appendTo(td); + + + $(tbl).append($(tr).append(td)).appendTo(div); + + // Append placeholder for list element + $('#vboxProgressOps').prepend($('<div />').addClass('vboxProgressOpElement').css({'display':'none'}).attr({'id':'vboxProgressPlaceholder'+pid})); + + $(div).data({ + 'vboxCallback':callback, + 'vboxIcon' : icon, + 'vboxTitle' : title, + 'vboxTarget' : target + }).dialog({'width':400,'height':'auto','closeOnEscape':false,'modal':true,'resizable':false,'draggable':true,'closeOnEscape':false,'buttons':{}}); + + +} + +/** + * Generate progress list element and append it + * + * @param {Object} prequest - progress operation request object + * @param {String} icon - URL of image to display on progress operation dialog (optional) + * @param {String} title - title of progress operation dialog (optional) + * @param {String} target - contextual target of progress operation + * @param {Function} callback - function to run on progress completion + * @see vboxconnector::progressGet() + */ +function vboxProgressCreateListElement(prequest,icon,title,target,callback) { + + // Shorthand + var pid = prequest.progress; + + var div = $('<div />').attr({'id':'vboxProgress'+pid}).addClass('vboxProgressOpElement'); + + var divOpTitle = $('<div />').addClass('vboxProgressOpTitle'); + + if(icon) { + $('<img />').attr({'src':'images/vbox/'+icon,'height':'16','width':'16'}).appendTo(divOpTitle); + } + + // Title + if($('#vboxPane').data('vboxConfig').servers.length) { + title = $('#vboxPane').data('vboxConfig').name + ': ' + title; + } + + $(divOpTitle).append(title + (target ? ' (' + target + ')' : '')).appendTo(div); + + // Progress bar + $('<div />').addClass('vboxProgressBarContainer').append( + $('<div />').attr({'id':'vboxProgressBar'+pid}).progressbar({ value: 1 }) + ).appendTo(div); + + // Progress text + $('<div />').addClass('vboxProgressOpText').append( + $('<span />').attr({'id':'vboxProgressText'+pid}).html('<img src="images/spinner.gif" height=12 width=12/>') + ).appendTo(div); + + // Cancel button + $('<div />').addClass('vboxProgressOpCancel').append( + $('<input />').attr({'id':'vboxProgressCancel'+pid,'type':'button'}).val(trans('Cancel','UIProgressDialog')).data({'pid':pid}) + .click(function(){ + this.disabled = 'disabled'; + vboxAjaxRequest('progressCancel',prequest); + }) + .css({'margin':'0px'}) + ).appendTo(div); + + $(div).data({'vboxCallback':callback}) + + if($('#vboxProgressPlaceholder'+pid)[0]) { + $('#vboxProgressPlaceholder'+pid).replaceWith(div); + } else { + $('#vboxProgressOps').prepend(div); + } + + +} + +/** + * OnUnload warning shown when an operation is in progress + * @return {String} warning message indicating operation is in progress + */ +function vboxOpInProgressCheck() { + if($('#vboxProgressOps').children('div.vboxProgressOpElement:not(.vboxProgressComplete)').addClass('vboxProgressRunning').length) { + 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'); + } +} + +/** + * Update progress dialog box. Callback run from vboxAjaxRequest + * + * @param {Object} prequest - progress operation data passed to ajax call + * @param {Object} data - data returned from progressGet AJAX call + */ +function vboxProgressUpdateModal(prequest, data) { + vboxProgressUpdate(prequest,data,true); +} + +/** + * Update progress dialog box or progress list row with % completed + * + * @param {Object} prequest - progress operation data passed to ajax call + * @param {Object} d - data returned from progressGet AJAX call + * @param {Boolean} modal - true if updating modal dialog + * @see vboxconnector::progressGet() + */ +function vboxProgressUpdate(prequest,d,modal) { + + // Shorthand + var pid = prequest.progress; + + // check for completed progress + if(!d || !d.responseData || !d.responseData['progress'] || !d.responseData['info'] || d.responseData['info']['completed'] || d.responseData['info']['canceled']) { + + if(d && d.responseData['info'] && d.responseData['info']['canceled']) + vboxAlert(trans('Operation Canceled','phpVirtualBox'),{'width':'300px','height':'auto'}); + + var callback = $("#vboxProgress"+pid).data('vboxCallback'); + + $("#vboxProgressBar"+pid).progressbar({ value: 100 }); + + if(modal) { + + var icon = $("#vboxProgress"+pid).data('vboxIcon'); + var title = $("#vboxProgress"+pid).data('vboxTitle'); + var target = $("#vboxProgress"+pid).data('vboxTarget'); + + $("#vboxProgress"+pid).empty().remove(); + + if(callback) callback(d); + + // Now append to list + vboxProgressCreateListElement(prequest,icon,title,target); + vboxProgressUpdate(prequest); + + } else { + + var sdate = new Date(); + $("#vboxProgressText"+pid).html(sdate.toLocaleString()); + $('#vboxProgressCancel'+pid).remove(); + + if(callback) callback(d); + } + + $("#vboxProgress"+pid).addClass('vboxProgressComplete').removeClass('vboxProgressRunning'); + + // Remove data + $("#vboxProgress"+pid).removeData([ + 'vboxCallback', + 'vboxIcon', + 'vboxTitle', + 'vboxTarget' + ]); + + // Check for max elements + if($('#vboxPane').data('vboxConfig').maxProgressList) { + var maxList = $('#vboxPane').data('vboxConfig').maxProgressList; + try { + maxList = Math.max(2,parseInt(maxList)); + } catch (e) { + maxList = 5; + } + if(maxList > 0) $('#vboxProgressOps').children('div.vboxProgressComplete').slice(maxList).remove(); + + } + + return; + } + + // update percent + $("#vboxProgressBar"+pid).progressbar({ value: d.responseData.info.percent }); + $("#vboxProgressText"+pid).html(d.responseData.info.percent+'%'+(modal ? '<br />' : ' ') + d.responseData.info.operationDescription); + + // Cancelable? + if(d.responseData.info.cancelable) { + $('#vboxProgressCancel'+pid).show(); + } + + // Get request + var def = $.Deferred(); + def.done(function(){ + + $.when(prequest, vboxAjaxRequest('progressGet', prequest, {'persist': d.persist})) + .done((modal ? vboxProgressUpdateModal : vboxProgressUpdate)); + + }); + window.setTimeout(def.resolve, 2000); + +} + +/** + * Position element to mouse event + * @param {HTMLNode} elm - HTML node to position + * @param {Event} e - Event to position to + */ +function vboxPositionEvent(elm,e) { + + var d = {}; + + if( self.innerHeight ) { + d.pageYOffset = self.pageYOffset; + d.pageXOffset = self.pageXOffset; + d.innerHeight = self.innerHeight; + d.innerWidth = self.innerWidth; + } else if( document.documentElement && + document.documentElement.clientHeight ) { + d.pageYOffset = document.documentElement.scrollTop; + d.pageXOffset = document.documentElement.scrollLeft; + d.innerHeight = document.documentElement.clientHeight; + d.innerWidth = document.documentElement.clientWidth; + } else if( document.body ) { + d.pageYOffset = document.body.scrollTop; + d.pageXOffset = document.body.scrollLeft; + d.innerHeight = document.body.clientHeight; + d.innerWidth = document.body.clientWidth; + } + + $(elm).css({'left':0,'top':0}); + + (e.pageX) ? x = e.pageX : x = e.clientX + d.scrollLeft; + (e.pageY) ? y = e.pageY : y = e.clientY + d.scrollTop; + + //adjust to ensure element is inside viewable screen + var right = x + $(elm).outerWidth(); + var bottom = y + $(elm).outerHeight(); + + var windowWidth = $(window).width() + $(window).scrollLeft()-5; + var windowHeight = $(window).height() + $(window).scrollTop()-5; + + x = (right > windowWidth) ? x - (right - windowWidth) : x; + y = (bottom > windowHeight) ? y - (bottom - windowHeight) : y; + + $(elm).css({ top: y, left: x }); +} + +/** + * Position element inside visible window + * @param {HTMLNode} elm - element + */ +function vboxPositionToWindow(elm) { + + var offset = $(elm).offset(); + var x = offset.left; + var y = offset.top; + + //adjust to ensure menu is inside viewable screen + var right = x + $(elm).outerWidth(); + var bottom = y + $(elm).outerHeight(); + + var windowWidth = $(window).width() + $(window).scrollLeft(); + var windowHeight = $(window).height() + $(window).scrollTop(); + + x = (right > windowWidth) ? x - (right - windowWidth) : x; + y = (bottom > windowHeight) ? y - (bottom - windowHeight) : y; + + $(elm).css({'top':y,'left':x}); + +} + +/* + * keycode input validation functions + */ +/** + * Return true if k param is a number + * @param {Integer} k - keycode + * @return {Boolean} + */ +function vboxValidateNum(k) { + return ((k >= 96 && k <= 105)||(k >= 48 && k <= 57)); +} +/** + * Return true if k param is a number or '.' + * @param {Integer} k - keycode + * @return {Boolean} + */ +function vboxValidateIP(k) { + return (vboxValidateNum(k) || k == 190 || k == 110 || k == 59 || k==78); +} +/** + * Return true if k param is a valid control code (shift, backspace, etc..) + * @param {Integer} k - keycode + * @return {Boolean} + */ +function vboxValidateCtrl(k) { + switch(k) { + case 8: // backspace + case 37: // left | right + case 39: + case 27: // esc + case 16: // shift + case 17: // ctrl + case 35: // end + case 36: // home + case 46: // del + case 144: // numlock + case 20: // capslock + case 18: // alt + return true; + } + return false; +} + +/** Parse Cookies and populate $('#vboxPane').data('vboxCookies') */ +function vboxParseCookies() { + if($('#vboxPane').data('vboxCookiesParsed')) return; + var cookies = {}; + var c = document.cookie.split('; '); + for(var i = 0; i < c.length; i++) { + var nv = c[i].split('='); + cookies[nv[0]] = nv[1]; + } + $('#vboxPane').data('vboxCookies', cookies); + $('#vboxPane').data('vboxCookiesParsed',true); +} + +/** + * General application failure + * @param {String|Object} msg - Optional extra message appended to error + * or error object passed to vboxAlert + */ +function phpVirtualBoxFailure(msg) { + if($('#vboxPane').data('vboxFatalError')) return; + $('#vboxPane').data('vboxFatalError', 1); + $('#vboxPane').css({'display':'none'}); + $('#vboxPane').trigger('phpVirtualBoxFailure'); + if(typeof(msg) == 'string') { + 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 : '')); + } 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.<p>The list of virtual machines will not begin auto-refreshing again until this page is reloaded.</p>','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; + +}); |