diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
commit | f8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch) | |
tree | 26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/ValidationKit/testmanager/htdocs/js | |
parent | Initial commit. (diff) | |
download | virtualbox-upstream.tar.xz virtualbox-upstream.zip |
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/ValidationKit/testmanager/htdocs/js')
3 files changed, 1476 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/testmanager/htdocs/js/Makefile.kup b/src/VBox/ValidationKit/testmanager/htdocs/js/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/ValidationKit/testmanager/htdocs/js/Makefile.kup diff --git a/src/VBox/ValidationKit/testmanager/htdocs/js/common.js b/src/VBox/ValidationKit/testmanager/htdocs/js/common.js new file mode 100644 index 00000000..eea71545 --- /dev/null +++ b/src/VBox/ValidationKit/testmanager/htdocs/js/common.js @@ -0,0 +1,1360 @@ +/* $Id: common.js $ */ +/** @file + * Common JavaScript functions + */ + +/* + * Copyright (C) 2012-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Same as WuiDispatcherBase.ksParamRedirectTo. */ +var g_ksParamRedirectTo = 'RedirectTo'; + + +/** + * Checks if the given value is a decimal integer value. + * + * @returns true if it is, false if it's isn't. + * @param sValue The value to inspect. + */ +function isInteger(sValue) +{ + if (typeof sValue != 'undefined') + { + var intRegex = /^\d+$/; + if (intRegex.test(sValue)) + { + return true; + } + } + return false; +} + +/** + * Checks if @a oMemmber is present in aoArray. + * + * @returns true/false. + * @param aoArray The array to check. + * @param oMember The member to check for. + */ +function isMemberOfArray(aoArray, oMember) +{ + var i; + for (i = 0; i < aoArray.length; i++) + if (aoArray[i] == oMember) + return true; + return false; +} + +/** + * Removes the element with the specified ID. + */ +function removeHtmlNode(sContainerId) +{ + var oElement = document.getElementById(sContainerId); + if (oElement) + { + oElement.parentNode.removeChild(oElement); + } +} + +/** + * Sets the value of the element with id @a sInputId to the keys of aoItems + * (comma separated). + */ +function setElementValueToKeyList(sInputId, aoItems) +{ + var sKey; + var oElement = document.getElementById(sInputId); + oElement.value = ''; + + for (sKey in aoItems) + { + if (oElement.value.length > 0) + { + oElement.value += ','; + } + + oElement.value += sKey; + } +} + +/** + * Get the Window.devicePixelRatio in a safe way. + * + * @returns Floating point ratio. 1.0 means it's a 1:1 ratio. + */ +function getDevicePixelRatio() +{ + var fpRatio = 1.0; + if (window.devicePixelRatio) + { + fpRatio = window.devicePixelRatio; + if (fpRatio < 0.5 || fpRatio > 10.0) + fpRatio = 1.0; + } + return fpRatio; +} + +/** + * Tries to figure out the DPI of the device in the X direction. + * + * @returns DPI on success, null on failure. + */ +function getDeviceXDotsPerInch() +{ + if (window.deviceXDPI && window.deviceXDPI > 48 && window.deviceXDPI < 2048) + { + return window.deviceXDPI; + } + else if (window.devicePixelRatio && window.devicePixelRatio >= 0.5 && window.devicePixelRatio <= 10.0) + { + cDotsPerInch = Math.round(96 * window.devicePixelRatio); + } + else + { + cDotsPerInch = null; + } + return cDotsPerInch; +} + +/** + * Gets the width of the given element (downscaled). + * + * Useful when using the element to figure the size of a image + * or similar. + * + * @returns Number of pixels. null if oElement is bad. + * @param oElement The element (not ID). + */ +function getElementWidth(oElement) +{ + if (oElement && oElement.offsetWidth) + return oElement.offsetWidth; + return null; +} + +/** By element ID version of getElementWidth. */ +function getElementWidthById(sElementId) +{ + return getElementWidth(document.getElementById(sElementId)); +} + +/** + * Gets the real unscaled width of the given element. + * + * Useful when using the element to figure the size of a image + * or similar. + * + * @returns Number of screen pixels. null if oElement is bad. + * @param oElement The element (not ID). + */ +function getUnscaledElementWidth(oElement) +{ + if (oElement && oElement.offsetWidth) + return Math.round(oElement.offsetWidth * getDevicePixelRatio()); + return null; +} + +/** By element ID version of getUnscaledElementWidth. */ +function getUnscaledElementWidthById(sElementId) +{ + return getUnscaledElementWidth(document.getElementById(sElementId)); +} + +/** + * Gets the part of the URL needed for a RedirectTo parameter. + * + * @returns URL string. + */ +function getCurrentBrowerUrlPartForRedirectTo() +{ + var sWhere = window.location.href; + var offTmp; + var offPathKeep; + + /* Find the end of that URL 'path' component. */ + var offPathEnd = sWhere.indexOf('?'); + if (offPathEnd < 0) + offPathEnd = sWhere.indexOf('#'); + if (offPathEnd < 0) + offPathEnd = sWhere.length; + + /* Go backwards from the end of the and find the start of the last component. */ + offPathKeep = sWhere.lastIndexOf("/", offPathEnd); + offTmp = sWhere.lastIndexOf(":", offPathEnd); + if (offPathKeep < offTmp) + offPathKeep = offTmp; + offTmp = sWhere.lastIndexOf("\\", offPathEnd); + if (offPathKeep < offTmp) + offPathKeep = offTmp; + + return sWhere.substring(offPathKeep + 1); +} + +/** + * Adds the given sorting options to the URL and reloads. + * + * This will preserve previous sorting columns except for those + * given in @a aiColumns. + * + * @param sParam Sorting parameter. + * @param aiColumns Array of sorting columns. + */ +function ahrefActionSortByColumns(sParam, aiColumns) +{ + var sWhere = window.location.href; + + var offHash = sWhere.indexOf('#'); + if (offHash < 0) + offHash = sWhere.length; + + var offQm = sWhere.indexOf('?'); + if (offQm > offHash) + offQm = -1; + + var sNew = ''; + if (offQm > 0) + sNew = sWhere.substring(0, offQm); + + sNew += '?' + sParam + '=' + aiColumns[0]; + var i; + for (i = 1; i < aiColumns.length; i++) + sNew += '&' + sParam + '=' + aiColumns[i]; + + if (offQm >= 0 && offQm + 1 < offHash) + { + var sArgs = '&' + sWhere.substring(offQm + 1, offHash); + var off = 0; + while (off < sArgs.length) + { + var offMatch = sArgs.indexOf('&' + sParam + '=', off); + if (offMatch >= 0) + { + if (off < offMatch) + sNew += sArgs.substring(off, offMatch); + + var offValue = offMatch + 1 + sParam.length + 1; + offEnd = sArgs.indexOf('&', offValue); + if (offEnd < offValue) + offEnd = sArgs.length; + + var iColumn = parseInt(sArgs.substring(offValue, offEnd)); + if (!isMemberOfArray(aiColumns, iColumn) && !isMemberOfArray(aiColumns, -iColumn)) + sNew += sArgs.substring(offMatch, offEnd); + + off = offEnd; + } + else + { + sNew += sArgs.substring(off); + break; + } + } + } + + if (offHash < sWhere.length) + sNew = sWhere.substr(offHash); + + window.location.href = sNew; +} + +/** + * Sets the value of an input field element (give by ID). + * + * @returns Returns success indicator (true/false). + * @param sFieldId The field ID (required for updating). + * @param sValue The field value. + */ +function setInputFieldValue(sFieldId, sValue) +{ + var oInputElement = document.getElementById(sFieldId); + if (oInputElement) + { + oInputElement.value = sValue; + return true; + } + return false; +} + +/** + * Adds a hidden input field to a form. + * + * @returns The new input field element. + * @param oFormElement The form to append it to. + * @param sName The field name. + * @param sValue The field value. + * @param sFieldId The field ID (optional). + */ +function addHiddenInputFieldToForm(oFormElement, sName, sValue, sFieldId) +{ + var oNew = document.createElement('input'); + oNew.type = 'hidden'; + oNew.name = sName; + oNew.value = sValue; + if (sFieldId) + oNew.id = sFieldId; + oFormElement.appendChild(oNew); + return oNew; +} + +/** By element ID version of addHiddenInputFieldToForm. */ +function addHiddenInputFieldToFormById(sFormId, sName, sValue, sFieldId) +{ + return addHiddenInputFieldToForm(document.getElementById(sFormId), sName, sValue, sFieldId); +} + +/** + * Adds or updates a hidden input field to/on a form. + * + * @returns The new input field element. + * @param sFormId The ID of the form to amend. + * @param sName The field name. + * @param sValue The field value. + * @param sFieldId The field ID (required for updating). + */ +function addUpdateHiddenInputFieldToFormById(sFormId, sName, sValue, sFieldId) +{ + var oInputElement = null; + if (sFieldId) + { + oInputElement = document.getElementById(sFieldId); + } + if (oInputElement) + { + oInputElement.name = sName; + oInputElement.value = sValue; + } + else + { + oInputElement = addHiddenInputFieldToFormById(sFormId, sName, sValue, sFieldId); + } + return oInputElement; +} + +/** + * Adds a width and a dpi input to the given form element if possible to + * determine the values. + * + * This is normally employed in an onlick hook, but then you must specify IDs or + * the browser may end up adding it several times. + * + * @param sFormId The ID of the form to amend. + * @param sWidthSrcId The ID of the element to calculate the width + * value from. + * @param sWidthName The name of the width value. + * @param sDpiName The name of the dpi value. + */ +function addDynamicGraphInputs(sFormId, sWidthSrcId, sWidthName, sDpiName) +{ + var cx = getUnscaledElementWidthById(sWidthSrcId); + var cDotsPerInch = getDeviceXDotsPerInch(); + + if (cx) + { + addUpdateHiddenInputFieldToFormById(sFormId, sWidthName, cx, sFormId + '-' + sWidthName + '-id'); + } + + if (cDotsPerInch) + { + addUpdateHiddenInputFieldToFormById(sFormId, sDpiName, cDotsPerInch, sFormId + '-' + sDpiName + '-id'); + } + +} + +/** + * Adds the RedirecTo field with the current URL to the form. + * + * This is a 'onsubmit' action. + * + * @returns Returns success indicator (true/false). + * @param oForm The form being submitted. + */ +function addRedirectToInputFieldWithCurrentUrl(oForm) +{ + /* Constant used here is duplicated in WuiDispatcherBase.ksParamRedirectTo */ + return addHiddenInputFieldToForm(oForm, 'RedirectTo', getCurrentBrowerUrlPartForRedirectTo(), null); +} + +/** + * Adds the RedirecTo parameter to the href of the given anchor. + * + * This is a 'onclick' action. + * + * @returns Returns success indicator (true/false). + * @param oAnchor The anchor element being clicked on. + */ +function addRedirectToAnchorHref(oAnchor) +{ + var sRedirectToParam = g_ksParamRedirectTo + '=' + encodeURIComponent(getCurrentBrowerUrlPartForRedirectTo()); + var sHref = oAnchor.href; + if (sHref.indexOf(sRedirectToParam) < 0) + { + var sHash; + var offHash = sHref.indexOf('#'); + if (offHash >= 0) + sHash = sHref.substring(offHash); + else + { + sHash = ''; + offHash = sHref.length; + } + sHref = sHref.substring(0, offHash) + if (sHref.indexOf('?') >= 0) + sHref += '&'; + else + sHref += '?'; + sHref += sRedirectToParam; + sHref += sHash; + oAnchor.href = sHref; + } + return true; +} + + + +/** + * Clears one input element. + * + * @param oInput The input to clear. + */ +function resetInput(oInput) +{ + switch (oInput.type) + { + case 'checkbox': + case 'radio': + oInput.checked = false; + break; + + case 'text': + oInput.value = 0; + break; + } +} + + +/** + * Clears a form. + * + * @param sIdForm The ID of the form + */ +function clearForm(sIdForm) +{ + var oForm = document.getElementById(sIdForm); + if (oForm) + { + var aoInputs = oForm.getElementsByTagName('INPUT'); + var i; + for (i = 0; i < aoInputs.length; i++) + resetInput(aoInputs[i]) + + /* HTML5 allows inputs outside <form>, so scan the document. */ + aoInputs = document.getElementsByTagName('INPUT'); + for (i = 0; i < aoInputs.length; i++) + if (aoInputs.hasOwnProperty("form")) + if (aoInputs.form == sIdForm) + resetInput(aoInputs[i]) + } + + return true; +} + + +/** @name Collapsible / Expandable items + * @{ + */ + + +/** + * Toggles the collapsible / expandable state of a parent DD and DT uncle. + * + * @returns true + * @param oAnchor The anchor object. + */ +function toggleCollapsibleDtDd(oAnchor) +{ + var oParent = oAnchor.parentElement; + var sClass = oParent.className; + + /* Find the DD sibling tag */ + var oDdElement = oParent.nextSibling; + while (oDdElement != null && oDdElement.tagName != 'DD') + oDdElement = oDdElement.nextSibling; + + /* Determin the new class and arrow char. */ + var sNewClass; + var sNewChar; + if ( sClass.substr(-11) == 'collapsible') + { + sNewClass = sClass.substr(0, sClass.length - 11) + 'expandable'; + sNewChar = '\u25B6'; /* black right-pointing triangle */ + } + else if (sClass.substr(-10) == 'expandable') + { + sNewClass = sClass.substr(0, sClass.length - 10) + 'collapsible'; + sNewChar = '\u25BC'; /* black down-pointing triangle */ + } + else + { + console.log('toggleCollapsibleParent: Invalid class: ' + sClass); + return true; + } + + /* Update the parent (DT) class and anchor text. */ + oParent.className = sNewClass; + oAnchor.firstChild.textContent = sNewChar + oAnchor.firstChild.textContent.substr(1); + + /* Update the uncle (DD) class. */ + if (oDdElement) + oDdElement.className = sNewClass; + return true; +} + +/** + * Shows/hides a sub-category UL according to checkbox status. + * + * The checkbox is expected to be within a label element or something. + * + * @returns true + * @param oInput The input checkbox. + */ +function toggleCollapsibleCheckbox(oInput) +{ + var oParent = oInput.parentElement; + + /* Find the UL sibling element. */ + var oUlElement = oParent.nextSibling; + while (oUlElement != null && oUlElement.tagName != 'UL') + oUlElement = oUlElement.nextSibling; + + /* Change the visibility. */ + if (oInput.checked) + oUlElement.className = oUlElement.className.replace('expandable', 'collapsible'); + else + { + oUlElement.className = oUlElement.className.replace('collapsible', 'expandable'); + + /* Make sure all sub-checkboxes are now unchecked. */ + var aoSubInputs = oUlElement.getElementsByTagName('input'); + var i; + for (i = 0; i < aoSubInputs.length; i++) + aoSubInputs[i].checked = false; + } + return true; +} + +/** + * Toggles the sidebar size so filters can more easily manipulated. + */ +function toggleSidebarSize() +{ + var sLinkText; + if (document.body.className != 'tm-wide-side-menu') + { + document.body.className = 'tm-wide-side-menu'; + sLinkText = '\u00ab\u00ab'; + } + else + { + document.body.className = ''; + sLinkText = '\u00bb\u00bb'; + } + + var aoToggleLink = document.getElementsByClassName('tm-sidebar-size-link'); + var i; + for (i = 0; i < aoToggleLink.length; i++) + if ( aoToggleLink[i].textContent.indexOf('\u00bb') >= 0 + || aoToggleLink[i].textContent.indexOf('\u00ab') >= 0) + aoToggleLink[i].textContent = sLinkText; +} + +/** @} */ + + +/** @name Custom Tooltips + * @{ + */ + +/** Where we keep tooltip elements when not displayed. */ +var g_dTooltips = {}; +var g_oCurrentTooltip = null; +var g_idTooltipShowTimer = null; +var g_idTooltipHideTimer = null; +var g_cTooltipSvnRevisions = 12; + +/** + * Cancel showing/replacing/repositing a tooltip. + */ +function tooltipResetShowTimer() +{ + if (g_idTooltipShowTimer) + { + clearTimeout(g_idTooltipShowTimer); + g_idTooltipShowTimer = null; + } +} + +/** + * Cancel hiding of the current tooltip. + */ +function tooltipResetHideTimer() +{ + if (g_idTooltipHideTimer) + { + clearTimeout(g_idTooltipHideTimer); + g_idTooltipHideTimer = null; + } +} + +/** + * Really hide the tooltip. + */ +function tooltipReallyHide() +{ + if (g_oCurrentTooltip) + { + //console.log('tooltipReallyHide: ' + g_oCurrentTooltip); + g_oCurrentTooltip.oElm.style.display = 'none'; + g_oCurrentTooltip = null; + } +} + +/** + * Schedule the tooltip for hiding. + */ +function tooltipHide() +{ + function tooltipDelayedHide() + { + tooltipResetHideTimer(); + tooltipReallyHide(); + } + + /* + * Cancel any pending show and schedule hiding if necessary. + */ + tooltipResetShowTimer(); + if (g_oCurrentTooltip && !g_idTooltipHideTimer) + { + g_idTooltipHideTimer = setTimeout(tooltipDelayedHide, 700); + } + + return true; +} + +/** + * Function that is repositions the tooltip when it's shown. + * + * Used directly, via onload, and hackish timers to catch all browsers and + * whatnot. + * + * Will set several tooltip member variables related to position and space. + */ +function tooltipRepositionOnLoad() +{ + if (g_oCurrentTooltip) + { + var oRelToRect = g_oCurrentTooltip.oRelToRect; + var cxNeeded = g_oCurrentTooltip.oElm.offsetWidth + 8; + var cyNeeded = g_oCurrentTooltip.oElm.offsetHeight + 8; + + var yScroll = window.pageYOffset || document.documentElement.scrollTop; + var yScrollBottom = yScroll + window.innerHeight; + var xScroll = window.pageXOffset || document.documentElement.scrollLeft; + var xScrollRight = xScroll + window.innerWidth; + + var cyAbove = Math.max(oRelToRect.top - yScroll, 0); + var cyBelow = Math.max(yScrollBottom - oRelToRect.bottom, 0); + var cxLeft = Math.max(oRelToRect.left - xScroll, 0); + var cxRight = Math.max(xScrollRight - oRelToRect.right, 0); + + var xPos; + var yPos; + + /* + * Decide where to put the thing. + */ + if (cyNeeded < cyBelow) + { + yPos = oRelToRect.bottom; + g_oCurrentTooltip.cyMax = cyBelow; + } + else if (cyBelow >= cyAbove) + { + yPos = yScrollBottom - cyNeeded; + g_oCurrentTooltip.cyMax = yScrollBottom - yPos; + } + else + { + yPos = oRelToRect.top - cyNeeded; + g_oCurrentTooltip.cyMax = yScrollBottom - yPos; + } + if (yPos < yScroll) + { + yPos = yScroll; + g_oCurrentTooltip.cyMax = yScrollBottom - yPos; + } + g_oCurrentTooltip.yPos = yPos; + g_oCurrentTooltip.yScroll = yScroll; + g_oCurrentTooltip.cyMaxUp = yPos - yScroll; + + if (cxNeeded < cxRight || cxNeeded > cxRight) + { + xPos = oRelToRect.right; + g_oCurrentTooltip.cxMax = cxRight; + } + else + { + xPos = oRelToRect.left - cxNeeded; + g_oCurrentTooltip.cxMax = cxNeeded; + } + g_oCurrentTooltip.xPos = xPos; + g_oCurrentTooltip.xScroll = xScroll; + + g_oCurrentTooltip.oElm.style.top = yPos + 'px'; + g_oCurrentTooltip.oElm.style.left = xPos + 'px'; + } + return true; +} + + +/** + * Really show the tooltip. + * + * @param oTooltip The tooltip object. + * @param oRelTo What to put the tooltip adjecent to. + */ +function tooltipReallyShow(oTooltip, oRelTo) +{ + var oRect; + + tooltipResetShowTimer(); + tooltipResetHideTimer(); + + if (g_oCurrentTooltip == oTooltip) + { + //console.log('moving tooltip'); + } + else if (g_oCurrentTooltip) + { + //console.log('removing current tooltip and showing new'); + tooltipReallyHide(); + } + else + { + //console.log('showing tooltip'); + } + + oTooltip.oElm.style.display = 'block'; + oTooltip.oElm.style.position = 'absolute'; + oRect = oRelTo.getBoundingClientRect(); + oTooltip.oRelToRect = oRect; + oTooltip.oElm.style.left = oRect.right + 'px'; + oTooltip.oElm.style.top = oRect.bottom + 'px'; + + g_oCurrentTooltip = oTooltip; + + /* + * This function does the repositioning at some point. + */ + tooltipRepositionOnLoad(); + if (oTooltip.oElm.onload === null) + { + oTooltip.oElm.onload = function(){ tooltipRepositionOnLoad(); setTimeout(tooltipRepositionOnLoad, 0); }; + } +} + +/** + * Tooltip onmouseenter handler . + */ +function tooltipElementOnMouseEnter() +{ + //console.log('tooltipElementOnMouseEnter: arguments.length='+arguments.length+' [0]='+arguments[0]); + //console.log('ENT: currentTarget='+arguments[0].currentTarget); + tooltipResetShowTimer(); + tooltipResetHideTimer(); + return true; +} + +/** + * Tooltip onmouseout handler. + * + * @remarks We only use this and onmouseenter for one tooltip element (iframe + * for svn, because chrome is sending onmouseout events after + * onmouseneter for the next element, which would confuse this simple + * code. + */ +function tooltipElementOnMouseOut() +{ + //console.log('tooltipElementOnMouseOut: arguments.length='+arguments.length+' [0]='+arguments[0]); + //console.log('OUT: currentTarget='+arguments[0].currentTarget); + tooltipHide(); + return true; +} + +/** + * iframe.onload hook that repositions and resizes the tooltip. + * + * This is a little hacky and we're calling it one or three times too many to + * work around various browser differences too. + */ +function svnHistoryTooltipOnLoad() +{ + //console.log('svnHistoryTooltipOnLoad'); + + /* + * Resize the tooltip to better fit the content. + */ + tooltipRepositionOnLoad(); /* Sets cxMax and cyMax. */ + if (g_oCurrentTooltip && g_oCurrentTooltip.oIFrame.contentWindow) + { + var oSubElement = g_oCurrentTooltip.oIFrame; + var cxSpace = Math.max(oSubElement.offsetLeft * 2, 0); /* simplified */ + var cySpace = Math.max(oSubElement.offsetTop * 2, 0); /* simplified */ + var cxNeeded = oSubElement.contentWindow.document.body.scrollWidth + cxSpace; + var cyNeeded = oSubElement.contentWindow.document.body.scrollHeight + cySpace; + var cx = Math.min(cxNeeded, g_oCurrentTooltip.cxMax); + var cy; + + g_oCurrentTooltip.oElm.width = cx + 'px'; + oSubElement.width = (cx - cxSpace) + 'px'; + if (cx >= cxNeeded) + { + //console.log('svnHistoryTooltipOnLoad: overflowX -> hidden'); + oSubElement.style.overflowX = 'hidden'; + } + else + { + oSubElement.style.overflowX = 'scroll'; + } + + cy = Math.min(cyNeeded, g_oCurrentTooltip.cyMax); + if (cyNeeded > g_oCurrentTooltip.cyMax && g_oCurrentTooltip.cyMaxUp > 0) + { + var cyMove = Math.min(cyNeeded - g_oCurrentTooltip.cyMax, g_oCurrentTooltip.cyMaxUp); + g_oCurrentTooltip.cyMax += cyMove; + g_oCurrentTooltip.yPos -= cyMove; + g_oCurrentTooltip.oElm.style.top = g_oCurrentTooltip.yPos + 'px'; + cy = Math.min(cyNeeded, g_oCurrentTooltip.cyMax); + } + + g_oCurrentTooltip.oElm.height = cy + 'px'; + oSubElement.height = (cy - cySpace) + 'px'; + if (cy >= cyNeeded) + { + //console.log('svnHistoryTooltipOnLoad: overflowY -> hidden'); + oSubElement.style.overflowY = 'hidden'; + } + else + { + oSubElement.style.overflowY = 'scroll'; + } + + //console.log('cyNeeded='+cyNeeded+' cyMax='+g_oCurrentTooltip.cyMax+' cySpace='+cySpace+' cy='+cy); + //console.log('oSubElement.offsetTop='+oSubElement.offsetTop); + //console.log('svnHistoryTooltipOnLoad: cx='+cx+'cxMax='+g_oCurrentTooltip.cxMax+' cxNeeded='+cxNeeded+' cy='+cy+' cyMax='+g_oCurrentTooltip.cyMax); + + tooltipRepositionOnLoad(); + } + return true; +} + +/** + * Calculates the last revision to get when showing a tooltip for @a iRevision. + * + * A tooltip covers several change log entries, both to limit the number of + * tooltips to load and to give context. The exact number is defined by + * g_cTooltipSvnRevisions. + * + * @returns Last revision in a tooltip. + * @param iRevision The revision number. + */ +function svnHistoryTooltipCalcLastRevision(iRevision) +{ + var iFirstRev = Math.floor(iRevision / g_cTooltipSvnRevisions) * g_cTooltipSvnRevisions; + return iFirstRev + g_cTooltipSvnRevisions - 1; +} + +/** + * Calculates a unique ID for the tooltip element. + * + * This is also used as dictionary index. + * + * @returns tooltip ID value (string). + * @param sRepository The repository name. + * @param iRevision The revision number. + */ +function svnHistoryTooltipCalcId(sRepository, iRevision) +{ + return 'svnHistoryTooltip_' + sRepository + '_' + svnHistoryTooltipCalcLastRevision(iRevision); +} + +/** + * The onmouseenter event handler for creating the tooltip. + * + * @param oEvt The event. + * @param sRepository The repository name. + * @param iRevision The revision number. + * @param sUrlPrefix URL prefix for non-testmanager use. + * + * @remarks onmouseout must be set to call tooltipHide. + */ +function svnHistoryTooltipShowEx(oEvt, sRepository, iRevision, sUrlPrefix) +{ + var sKey = svnHistoryTooltipCalcId(sRepository, iRevision); + var oTooltip = g_dTooltips[sKey]; + var oParent = oEvt.currentTarget; + //console.log('svnHistoryTooltipShow ' + sRepository); + + function svnHistoryTooltipDelayedShow() + { + var oSubElement; + var sSrc; + + oTooltip = g_dTooltips[sKey]; + //console.log('svnHistoryTooltipDelayedShow ' + sRepository + ' ' + oTooltip); + if (!oTooltip) + { + /* + * Create a new tooltip element. + */ + //console.log('creating ' + sKey); + oTooltip = {}; + oTooltip.oElm = document.createElement('div'); + oTooltip.oElm.setAttribute('id', sKey); + oTooltip.oElm.setAttribute('class', 'tmvcstooltip'); + oTooltip.oElm.style.position = 'absolute'; + oTooltip.oElm.style.zIndex = 6001; + oTooltip.xPos = 0; + oTooltip.yPos = 0; + oTooltip.cxMax = 0; + oTooltip.cyMax = 0; + oTooltip.cyMaxUp = 0; + oTooltip.xScroll = 0; + oTooltip.yScroll = 0; + + oSubElement = document.createElement('iframe'); + oSubElement.setAttribute('id', sKey + '_iframe'); + oSubElement.setAttribute('style', 'position: relative;"'); + oSubElement.onload = function() {svnHistoryTooltipOnLoad(); setTimeout(svnHistoryTooltipOnLoad,0);}; + oSubElement.onmouseenter = tooltipElementOnMouseEnter; + oSubElement.onmouseout = tooltipElementOnMouseOut; + oTooltip.oElm.appendChild(oSubElement); + oTooltip.oIFrame = oSubElement; + g_dTooltips[sKey] = oTooltip; + + document.body.appendChild(oTooltip.oElm); + } + else + { + oSubElement = oTooltip.oIFrame; + } + + oSubElement.setAttribute('src', sUrlPrefix + 'index.py?Action=VcsHistoryTooltip&repo=' + sRepository + + '&rev=' + svnHistoryTooltipCalcLastRevision(iRevision) + + '&cEntries=' + g_cTooltipSvnRevisions + + '#r' + iRevision); + tooltipReallyShow(oTooltip, oParent); + /* Resize and repositioning hacks. */ + svnHistoryTooltipOnLoad(); + setTimeout(svnHistoryTooltipOnLoad, 0); + } + + /* + * Delay the change. + */ + tooltipResetShowTimer(); + g_idTooltipShowTimer = setTimeout(svnHistoryTooltipDelayedShow, 512); +} + +/** + * The onmouseenter event handler for creating the tooltip. + * + * @param oEvt The event. + * @param sRepository The repository name. + * @param iRevision The revision number. + * + * @remarks onmouseout must be set to call tooltipHide. + */ +function svnHistoryTooltipShow(oEvt, sRepository, iRevision) +{ + return svnHistoryTooltipShowEx(oEvt, sRepository, iRevision, '') +} + +/** @} */ + + +/** @name Debugging and Introspection + * @{ + */ + +/** + * Python-like dir() implementation. + * + * @returns Array of names associated with oObj. + * @param oObj The object under inspection. If not specified we'll + * look at the window object. + */ +function pythonlikeDir(oObj, fDeep) +{ + var aRet = []; + var dTmp = {}; + + if (!oObj) + { + oObj = window; + } + + for (var oCur = oObj; oCur; oCur = Object.getPrototypeOf(oCur)) + { + var aThis = Object.getOwnPropertyNames(oCur); + for (var i = 0; i < aThis.length; i++) + { + if (!(aThis[i] in dTmp)) + { + dTmp[aThis[i]] = 1; + aRet.push(aThis[i]); + } + } + } + + return aRet; +} + + +/** + * Python-like dir() implementation, shallow version. + * + * @returns Array of names associated with oObj. + * @param oObj The object under inspection. If not specified we'll + * look at the window object. + */ +function pythonlikeShallowDir(oObj, fDeep) +{ + var aRet = []; + var dTmp = {}; + + if (oObj) + { + for (var i in oObj) + { + aRet.push(i); + } + } + + return aRet; +} + + + +function dbgGetObjType(oObj) +{ + var sType = typeof oObj; + if (sType == "object" && oObj !== null) + { + if (oObj.constructor && oObj.constructor.name) + { + sType = oObj.constructor.name; + } + else + { + var fnToString = Object.prototype.toString; + var sTmp = fnToString.call(oObj); + if (sTmp.indexOf('[object ') === 0) + { + sType = sTmp.substring(8, sTmp.length); + } + } + } + return sType; +} + + +/** + * Dumps the given object to the console. + * + * @param oObj The object under inspection. + * @param sPrefix What to prefix the log output with. + */ +function dbgDumpObj(oObj, sName, sPrefix) +{ + var aMembers; + var sType; + + /* + * Defaults + */ + if (!oObj) + { + oObj = window; + } + + if (!sPrefix) + { + if (sName) + { + sPrefix = sName + ':'; + } + else + { + sPrefix = 'dbgDumpObj:'; + } + } + + if (!sName) + { + sName = ''; + } + + /* + * The object itself. + */ + sPrefix = sPrefix + ' '; + console.log(sPrefix + sName + ' ' + dbgGetObjType(oObj)); + + /* + * The members. + */ + sPrefix = sPrefix + ' '; + aMembers = pythonlikeDir(oObj); + for (i = 0; i < aMembers.length; i++) + { + console.log(sPrefix + aMembers[i]); + } + + return true; +} + +function dbgDumpObjWorker(sType, sName, oObj, sPrefix) +{ + var sRet; + switch (sType) + { + case 'function': + { + sRet = sPrefix + 'function ' + sName + '()' + '\n'; + break; + } + + case 'object': + { + sRet = sPrefix + 'var ' + sName + '(' + dbgGetObjType(oObj) + ') ='; + if (oObj !== null) + { + sRet += '\n'; + } + else + { + sRet += ' null\n'; + } + break; + } + + case 'string': + { + sRet = sPrefix + 'var ' + sName + '(string, ' + oObj.length + ')'; + if (oObj.length < 80) + { + sRet += ' = "' + oObj + '"\n'; + } + else + { + sRet += '\n'; + } + break; + } + + case 'Oops!': + sRet = sPrefix + sName + '(??)\n'; + break; + + default: + sRet = sPrefix + 'var ' + sName + '(' + sType + ')\n'; + break; + } + return sRet; +} + + +function dbgObjInArray(aoObjs, oObj) +{ + var i = aoObjs.length; + while (i > 0) + { + i--; + if (aoObjs[i] === oObj) + { + return true; + } + } + return false; +} + +function dbgDumpObjTreeWorker(oObj, sPrefix, aParentObjs, cMaxDepth) +{ + var sRet = ''; + var aMembers = pythonlikeShallowDir(oObj); + var i; + + for (i = 0; i < aMembers.length; i++) + { + //var sName = i; + var sName = aMembers[i]; + var oMember; + var sType; + var oEx; + + try + { + oMember = oObj[sName]; + sType = typeof oObj[sName]; + } + catch (oEx) + { + oMember = null; + sType = 'Oops!'; + } + + //sRet += '[' + i + '/' + aMembers.length + ']'; + sRet += dbgDumpObjWorker(sType, sName, oMember, sPrefix); + + if ( sType == 'object' + && oObj !== null) + { + + if (dbgObjInArray(aParentObjs, oMember)) + { + sRet += sPrefix + '! parent recursion\n'; + } + else if ( sName == 'previousSibling' + || sName == 'previousElement' + || sName == 'lastChild' + || sName == 'firstElementChild' + || sName == 'lastElementChild' + || sName == 'nextElementSibling' + || sName == 'prevElementSibling' + || sName == 'parentElement' + || sName == 'ownerDocument') + { + sRet += sPrefix + '! potentially dangerous element name\n'; + } + else if (aParentObjs.length >= cMaxDepth) + { + sRet = sRet.substring(0, sRet.length - 1); + sRet += ' <too deep>!\n'; + } + else + { + + aParentObjs.push(oMember); + if (i + 1 < aMembers.length) + { + sRet += dbgDumpObjTreeWorker(oMember, sPrefix + '| ', aParentObjs, cMaxDepth); + } + else + { + sRet += dbgDumpObjTreeWorker(oMember, sPrefix.substring(0, sPrefix.length - 2) + ' | ', aParentObjs, cMaxDepth); + } + aParentObjs.pop(); + } + } + } + return sRet; +} + +/** + * Dumps the given object and all it's subobjects to the console. + * + * @returns String dump of the object. + * @param oObj The object under inspection. + * @param sName The object name (optional). + * @param sPrefix What to prefix the log output with (optional). + * @param cMaxDepth The max depth, optional. + */ +function dbgDumpObjTree(oObj, sName, sPrefix, cMaxDepth) +{ + var sType; + var sRet; + var oEx; + + /* + * Defaults + */ + if (!sPrefix) + { + sPrefix = ''; + } + + if (!sName) + { + sName = '??'; + } + + if (!cMaxDepth) + { + cMaxDepth = 2; + } + + /* + * The object itself. + */ + try + { + sType = typeof oObj; + } + catch (oEx) + { + sType = 'Oops!'; + } + sRet = dbgDumpObjWorker(sType, sName, oObj, sPrefix); + if (sType == 'object' && oObj !== null) + { + var aParentObjs = Array(); + aParentObjs.push(oObj); + sRet += dbgDumpObjTreeWorker(oObj, sPrefix + '| ', aParentObjs, cMaxDepth); + } + + return sRet; +} + +function dbgLogString(sLongString) +{ + var aStrings = sLongString.split("\n"); + var i; + for (i = 0; i < aStrings.length; i++) + { + console.log(aStrings[i]); + } + console.log('dbgLogString - end - ' + aStrings.length + '/' + sLongString.length); + return true; +} + +function dbgLogObjTree(oObj, sName, sPrefix, cMaxDepth) +{ + return dbgLogString(dbgDumpObjTree(oObj, sName, sPrefix, cMaxDepth)); +} + +/** @} */ + diff --git a/src/VBox/ValidationKit/testmanager/htdocs/js/graphwiz.js b/src/VBox/ValidationKit/testmanager/htdocs/js/graphwiz.js new file mode 100644 index 00000000..eeb81b7f --- /dev/null +++ b/src/VBox/ValidationKit/testmanager/htdocs/js/graphwiz.js @@ -0,0 +1,116 @@ +/* $Id: graphwiz.js $ */ +/** @file + * JavaScript functions for the Graph Wizard. + */ + +/* + * Copyright (C) 2012-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** The previous width of the div element that we measure. */ +var g_cxPreviousWidth = 0; + + +/** + * onload function that sets g_cxPreviousWidth to the width of @a sWidthSrcId. + * + * @returns true. + * @param sWidthSrcId The ID of the element which width we should measure. + */ +function graphwizOnLoadRememberWidth(sWidthSrcId) +{ + var cx = getUnscaledElementWidthById(sWidthSrcId); + if (cx) + { + g_cxPreviousWidth = cx; + } + return true; +} + + +/** + * onresize callback function that scales the given graph width input field + * value according to the resized element. + * + * @returns true. + * @param sWidthSrcId The ID of the element which width we should measure + * the resize effect on. + * @param sWidthInputId The ID of the input field which values should be + * scaled. + * + * @remarks Since we're likely to get several resize calls as part of one user + * resize operation, we're likely to suffer from some rounding + * artifacts. So, should the user abort or undo the resizing, the + * width value is unlikely to be restored to the exact value it had + * prior to the resizing. + */ +function graphwizOnResizeRecalcWidth(sWidthSrcId, sWidthInputId) +{ + var cx = getUnscaledElementWidthById(sWidthSrcId); + if (cx) + { + var oElement = document.getElementById(sWidthInputId); + if (oElement && g_cxPreviousWidth) + { + var cxOld = oElement.value; + if (isInteger(cxOld)) + { + var fpRatio = cxOld / g_cxPreviousWidth; + oElement.value = Math.round(cx * fpRatio); + } + } + g_cxPreviousWidth = cx; + } + + return true; +} + +/** + * Fills thegraph size (cx, cy) and dpi fields with default values. + * + * @returns false (for onclick). + * @param sWidthSrcId The ID of the element which width we should measure. + * @param sWidthInputId The ID of the graph width field (cx). + * @param sHeightInputId The ID of the graph height field (cy). + * @param sDpiInputId The ID of the graph DPI field. + */ +function graphwizSetDefaultSizeValues(sWidthSrcId, sWidthInputId, sHeightInputId, sDpiInputId) +{ + var cx = getUnscaledElementWidthById(sWidthSrcId); + var cDotsPerInch = getDeviceXDotsPerInch(); + + if (cx) + { + setInputFieldValue(sWidthInputId, cx); + setInputFieldValue(sHeightInputId, Math.round(cx * 5 / 16)); /* See wuimain.py. */ + } + + if (cDotsPerInch) + { + setInputFieldValue(sDpiInputId, cDotsPerInch); + } + + return false; +} + |